java查缺补漏

快来打我* 2022-04-17 04:24 334阅读 0赞

最近有个需求要把项目发布在weblogic12c服务器中,按照网上的教程对springboot项目进行改造后,陆续遇到了几个问题:
1.weblogic报错:afu.com.sun.source.tree.Tree$Kind?

  1. 这个错误 把项目中用到的依赖guava版本更换为24.1-jre解决

2.controller层返回数据的时候,报Jackson转换的错误,nosuchMethod 之类的,

  1. spring-boot-starter-web中的Jackson-core排除掉,然后把json解析换成了fastJson解决。

3.Java将图片转化为二进制字符格式而不是二进制流,不转成bitmap数组格式的?

  1. import java.awt.image.BufferedImage;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import javax.imageio.ImageIO;
  7. import sun.misc.BASE64Decoder;
  8. import sun.misc.BASE64Encoder;
  9. public class TestImageBinary {
  10. static BASE64Encoder encoder = new sun.misc.BASE64Encoder();
  11. static BASE64Decoder decoder = new sun.misc.BASE64Decoder();
  12. public static void main(String[] args) {
  13. System.out.println(getImageBinary());
  14. base64StringToImage(getImageBinary());
  15. }
  16. static String getImageBinary(){
  17. File f = new File("f://123456.jpg"); //这里gif动态图不可以,虽然在后面也能输出gif格式,但是却不是动图
  18. BufferedImage bi;
  19. try {
  20. bi = ImageIO.read(f);
  21. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  22. ImageIO.write(bi, "jpg", baos);
  23. byte[] bytes = baos.toByteArray();
  24. return encoder.encodeBuffer(bytes).trim();
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. return null;
  29. }
  30. static void base64StringToImage(String base64String){
  31. try {
  32. byte[] bytes1 = decoder.decodeBuffer(base64String);
  33. ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
  34. BufferedImage bi1 =ImageIO.read(bais);
  35. File w2 = new File("f://meinv.bmp");//可以是jpg,png格式
  36. ImageIO.write(bi1, "jpg", w2);//不管输出什么格式图片,此处不需改动
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. }

4.Java的的反射实现是委派实现还是动态实现?委派实现和动态实现是按照什么机制去识别实现的?即什么情况下用委派实现,什么情况下动态实现?

  1. 如果对什么是线程、什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内。
  2. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
  3. 很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。
  4. 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
  5. 并行与并发:
  6. 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
  7. 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
  8. 并发与并行
  9. 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
  10. void transferMoney(User from, User to, float amount){
  11. to.setMoney(to.getBalance() + amount);
  12. from.setMoney(from.getBalance() - amount);
  13. }
  14. 同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
  15. 好了,让我们开始吧。我准备分成几部分来总结涉及到多线程的内容:
  16. 扎好马步:线程的状态
  17. 内功心法:每个对象都有的方法(机制)
  18. 太祖长拳:基本线程类
  19. 九阴真经:高级多线程控制类
  20. 扎好马步:线程的状态
  21. 先来两张图:
  22. 线程状态
  23. 线程状态转换
  24. 各种状态一目了然,值得一提的是"blocked"这个状态:
  25. 线程在Running的过程中可能会遇到阻塞(Blocked)情况
  26. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
  27. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable
  28. Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。
  29. 此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable
  30. 内功心法:每个对象都有的方法(机制)
  31. synchronized, wait, notify 是任何对象都具有的同步工具。让我们先来了解他们
  32. monitor
  33. 他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。
  34. wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。
  35. 当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去waitnotify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
  36. 再讲用法:
  37. synchronized单独使用:
  38. 代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容
  39. public class Thread1 implements Runnable {
  40. Object lock;
  41. public void run() {
  42. synchronized(lock){
  43. ..do something
  44. }
  45. }
  46. }
  47. 直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。
  48. public class Thread1 implements Runnable {
  49. public synchronized void run() {
  50. ..do something
  51. }
  52. }
  53. synchronized, wait, notify结合:典型场景生产者消费者问题
  54. /**
  55. * 生产者生产出来的产品交给店员
  56. */
  57. public synchronized void produce()
  58. {
  59. if(this.product >= MAX_PRODUCT)
  60. {
  61. try
  62. {
  63. wait();
  64. System.out.println("产品已满,请稍候再生产");
  65. }
  66. catch(InterruptedException e)
  67. {
  68. e.printStackTrace();
  69. }
  70. return;
  71. }
  72. this.product++;
  73. System.out.println("生产者生产第" + this.product + "个产品.");
  74. notifyAll(); //通知等待区的消费者可以取出产品了
  75. }
  76. /**
  77. * 消费者从店员取产品
  78. */
  79. public synchronized void consume()
  80. {
  81. if(this.product <= MIN_PRODUCT)
  82. {
  83. try
  84. {
  85. wait();
  86. System.out.println("缺货,稍候再取");
  87. }
  88. catch (InterruptedException e)
  89. {
  90. e.printStackTrace();
  91. }
  92. return;
  93. }
  94. System.out.println("消费者取走了第" + this.product + "个产品.");
  95. this.product--;
  96. notifyAll(); //通知等待去的生产者可以生产产品了
  97. }
  98. volatile
  99. 多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
  100. volatile
  101. 针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。
  102. 太祖长拳:基本线程类
  103. 基本线程类指的是Thread类,Runnable接口,Callable接口
  104. Thread 类实现了Runnable接口,启动一个线程的方法:
  105.  MyThread my = new MyThread();
  106.   my.start();
  107. Thread类相关方法:
  108. //当前线程可转让cpu控制权,让别的就绪状态线程运行(切换)
  109. public static Thread.yield()
  110. //暂停一段时间
  111. public static Thread.sleep()
  112. //在一个线程中调用other.join(),将等待other执行完后才继续本线程。    
  113. public join()
  114. //后两个函数皆可以被打断
  115. public interrupte()
  116. 关于中断:它并不像stop方法那样会中断一个正在运行的线程。线程会不时地检测中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。终端只会影响到wait状态、sleep状态和join状态。被打断的线程会抛出InterruptedException
  117. Thread.interrupted()检查当前线程是否发生中断,返回boolean
  118. synchronized在获锁的过程中是不能被中断的。
  119. 中断是一个状态!interrupt()方法只是将这个状态置为true而已。所以说正常运行的程序不去检测状态,就不会终止,而wait等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体
  120. Thread类最佳实践:
  121. 写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。
  122. 如何获取线程中的异常
  123. 不能用try,catch来获取线程中的异常
  124. Runnable
  125. Thread类似
  126. Callable
  127. future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDoneget。其中Future对象用来存放该线程的返回值以及状态
  128. ExecutorService e = Executors.newFixedThreadPool(3);
  129. //submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
  130. Future future = e.submit(new myCallable());
  131. future.isDone() //return true,false 无阻塞
  132. future.get() // return 返回值,阻塞直到该线程运行结束
  133. 九阴真经:高级多线程控制类
  134. 以上都属于内功心法,接下来是实际项目中常用到的工具了,Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。
  135. 1.ThreadLocal
  136. 用处:保存线程的独立变量。对一个线程类(继承自Thread)
  137. 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。
  138. 实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value
  139. 主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> aget时将a返回。ThreadLocal是一个特殊的容器。
  140. 2.原子类(AtomicIntegerAtomicBoolean……)
  141. 如果使用atomic wrapper classatomicInteger,或者使用自己保证原子的操作,则等同于synchronized
  142. //返回值为boolean
  143. AtomicInteger.compareAndSet(int expect,int update)
  144. 该方法可用于实现乐观锁,考虑文中最初提到的如下场景:ab付款10元,a扣了10元,b要加10元。此时cb2元,但是b的加十元代码约为:
  145. if(b.value.compareAndSet(old, value)){
  146. return ;
  147. }else{
  148. //try again
  149. // if that fails, rollback and log
  150. }
  151. AtomicReference
  152. 对于AtomicReference 来讲,也许对象会出现,属性丢失的情况,即oldObject == current,但是oldObject.getPropertyA != current.getPropertyA
  153. 这时候,AtomicStampedReference就派上用场了。这也是一个很常用的思路,即加上版本号
  154. 3.Lock类 
  155. lock: java.util.concurrent包内。共有三个实现:
  156. ReentrantLock
  157. ReentrantReadWriteLock.ReadLock
  158. ReentrantReadWriteLock.WriteLock
  159. 主要目的是和synchronized一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有一些区别。
  160. 区别如下:
  161. lock更灵活,可以自由定义多把锁的枷锁解锁顺序(synchronized要按照先加的后解顺序)
  162. 提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有trylock的带超时时间版本。
  163. 本质上和监视器锁(即synchronized是一样的)
  164. 能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。
  165. Condition类的结合。
  166. 性能更高,对比如下图:
  167. synchronizedLock性能对比
  168. ReentrantLock    
  169. 可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。
  170. 使用方法是:
  171. 1.new一个实例
  172. static ReentrantLock r=new ReentrantLock();
  173. 2.加锁      
  174. r.lock()或r.lockInterruptibly();
  175. 此处也是个不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockInterruptibly,那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争抢,进入catch块。(如果使用后者,必须throw interruptable exception catch)    
  176. 3.释放锁   
  177. r.unlock()
  178. 必须做!何为必须做呢,要放在finally里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally是可以信任的:经过测试,哪怕是发生了OutofMemoryErrorfinally块中的语句执行也能够得到保证。
  179. ReentrantReadWriteLock
  180. 可重入读写锁(读写锁的一个实现) 
  181.  ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
  182.   ReadLock r = lock.readLock();
  183.   WriteLock w = lock.writeLock();
  184. 两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码
  185. 4.容器类
  186. 这里就讨论比较常用的两个:
  187. BlockingQueue
  188. ConcurrentHashMap
  189. BlockingQueue
  190. 阻塞队列。该类是java.util.concurrent包下的重要类,通过对Queue的学习可以得知,这个queue是单向队列,可以在队列头添加元素和在队尾删除或取出元素。类似于一个管  道,特别适用于先进先出策略的一些应用场景。普通的queue接口主要实现有PriorityQueue(优先队列),有兴趣可以研究
  191. BlockingQueue在队列的基础上添加了多线程协作的功能:
  192. BlockingQueue
  193. 除了传统的queue功能(表格左边的两列)之外,还提供了阻塞接口puttake,带超时功能的阻塞接口offerpollput会在队列满的时候阻塞,直到有空间时被唤醒;take在队 列空的时候阻塞,直到有东西拿的时候才被唤醒。用于生产者-消费者模型尤其好用,堪称神器。
  194. 常见的阻塞队列有:
  195. ArrayListBlockingQueue
  196. LinkedListBlockingQueue
  197. DelayQueue
  198. SynchronousQueue
  199. ConcurrentHashMap
  200. 高效的线程安全哈希map。请对比hashTable , concurrentHashMap, HashMap
  201. 5.管理类
  202. 管理类的概念比较泛,用于管理线程,本身不是多线程的,但提供了一些机制来利用上述的工具做一些封装。
  203. 了解到的值得一提的管理类:ThreadPoolExecutor JMX框架下的系统级管理类 ThreadMXBean
  204. ThreadPoolExecutor
  205. 如果不了解这个类,应该了解前面提到的ExecutorService,开一个自己的线程池非常方便:
  206. ExecutorService e = Executors.newCachedThreadPool();
  207. ExecutorService e = Executors.newSingleThreadExecutor();
  208. ExecutorService e = Executors.newFixedThreadPool(3);
  209. // 第一种是可变大小线程池,按照任务数来分配线程,
  210. // 第二种是单线程池,相当于FixedThreadPool(1)
  211. // 第三种是固定大小线程池。
  212. // 然后运行
  213. e.execute(new MyRunnableImpl());
  214. 该类内部是通过ThreadPoolExecutor实现的,掌握该类有助于理解线程池的管理,本质上,他们都是ThreadPoolExecutor类的各种实现版本。请参见javadoc
  215. ThreadPoolExecutor参数解释
  216. 翻译一下:
  217. corePoolSize:池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。
  218. maximumPoolSize:线程最大值,线程的增长始终不会超过该值。
  219. keepAliveTime:当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态
  220. unit
  221. 时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS 
  222. workQueue:待入任务(Runnable)的等待场所,该参数主要影响调度策略,如公平与否,是否产生饿死(starving)
  223. threadFactory:线程工厂类,有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。

6.想做延迟任务,比如今天下发任务到每个单位,截止明天上午10点必须汇报,否则就短信提示。想使用rabbitmq实现,目前了解到rabbitmq队列,昨天在先队列中放了10点结束的,今天放入队列一个9点的,但是由于队列的特性,只能等10点先出队列,九点的才能出来,又不想用对个队列接受不同时间的的任务,请问有别的方案吗?

  1. 可以用 Dead Letter Exchanges 来实现。
  2. 使用RabbitMQ实现延迟任务场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时。
  3. 场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单。上述类似的需求是我们经常会遇见的问题。最常用的方法是定期轮训数据库,设置状态。在数据量小的时候并没有什么大的问题,但是数据量一大轮训数据库的方式就会变得特别耗资源。当面对千万级、上亿级数据量时,本身写入的IO就比较高,导致长时间查询或者根本就查不出来,更别说分库分表以后了。
  4. 除此之外,还有优先级队列,基于优先级队列的JDK延迟队列,时间轮等方式。但如果系统的架构中本身就有RabbitMQ的话,那么选择RabbitMQ来实现类似的功能也是一种选择。使用RabbitMQ来实现延迟任务必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求。消息的TTLTime To Live)消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL
  5. 对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不一样(不同的队列设置)。这里单讲单个消息的TTL,因为它才是实现延迟任务的关键。可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是一样的效果。只是expiration字段是字符串参数,所以要写个int类型的字符串:byte[] messageBodyBytes = "Hello, world!".getBytes();AMQP.BasicProperties properties = new AMQP.BasicProperties();properties.setExpiration("60000");channel.basicPublish("my-exchange", "routing-key", properties, messageBodyBytes);当上面的消息扔到队列中后,过了60秒,如果没有被消费,它就死了。不会被消费者消费到。这个消息后面的,没有“死掉”的消息对顶上来,被消费者消费。死信在队列中并不会被删除和释放,它会被统计到队列的消息数中去。单靠死信还不能实现延迟任务,还要靠Dead Letter ExchangeDead Letter ExchangesExchage的概念在这里就不在赘述,可以从这里进行了解。一个消息在满足如下条件下,会进死信路由,记住这里是路由而不是队列,一个路由可以对应很多队列。1. 一个消息被Consumer拒收了,并且reject方法的参数里requeuefalse。也就是说不会被再次放在队列里,被其他消费者使用。2. 上面的消息的TTL到了,消息过期了。3. 队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。Dead Letter Exchange其实就是一种普通的exchange,和创建其他exchange没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。实现延迟队列延迟任务通过消息的TTLDead Letter Exchange来实现。我们需要建立2个队列,一个用于发送消息,一个用于消息过期后的转发目标队列。 生产者输出消息到Queue1,并且这个消息是设置有有效时间的,比如60s。消息会在Queue1中等待60s,如果没有消费者收掉的话,它就是被转发到Queue2Queue2有消费者,收到,处理延迟任务。具体实现步骤如下:第一步, 首先需要创建2个队列。Queue1Queue2Queue1是一个消息缓冲队列,在这个队列里面实现消息的过期转发。如下图,设置Dead letter exchangeDead letter routing key。设置这两个属性就是当消息在这个队列中expire后,采用哪个路由发送。这个dlxexchange需要事先创建好,就是一个普通的exchange。由于我们还需要向Queue1发送消息,那么还需要创建一个exchange,并且和Queue1绑定。例子中,exchange同样取名:queue1。我们还需要建一个Queue2,这个队列用于消息在Queue1中过期后转发的目标队列。所以这个Queue2队列建好以后,需要绑定Queue1设置的死信路由:dlx。完成Queue2的绑定以后,环境就搭建完成了。 第二步,实现消息的Producer。由于我们的目的是让进入Queue1的消息过期,然后自动转送到Queue2中,所以发送的时候,需要设置过期时间。ConnectionFactory factory = new ConnectionFactory(); factory.setUsername("bsp"); factory.setPassword("123456"); factory.setVirtualHost("/"); factory.setHost("10.23.22.42"); factory.setPort(5672); conn = factory.newConnection(); channel = conn.createChannel(); byte[] messageBodyBytes = "Hello, world!".getBytes(); byte i = 10; while (i-- > 0) { channel.basicPublish("queue1", "queue1", new AMQP.BasicProperties.Builder().expiration(String.valueOf(i * 1000)).build(), new byte[] { i }); }上面的代码我模拟了1-10号消息,消息的内容里面是1-10。过期的时间是10-1秒。这里要注意,虽然10是第一个发送,但是它过期的时间最长。 第三步,实现消息的ConsumerConsumer就是延迟任务的具体实施者。由于具体的任务往往是一个比较耗时的任务,所以一般来说,任务一般在异步线程中执行。ConnectionFactory factory = new ConnectionFactory();factory.setUsername("bsp");factory.setPassword("123456");factory.setVirtualHost("/");factory.setHost("10.23.22.42");factory.setPort(5672);conn = factory.newConnection();channel = conn.createChannel();channel.basicConsume("queue2", true, "consumer", new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException {long deliveryTag = envelope.getDeliveryTag(); //do some work asyncSystem.out.println(body[0]);}}); 运行后如上面的程序,过了10s以后,消费者开始收到数据,但是它是一次性收到如下结果:10、9 、8 、7 、6、5 、4 、3 、2 、1Consumer第一个收到的还是10。虽然10是第一个放进队列,但是它的过期时间最长。所以由此可见,即使一个消息比在同一队列中的其他消息提前过期,提前过期的也不会优先进入死信队列,它们还是按照入库的顺序让消费者消费。如果第一进去的消息过期时间是1小时,那么死信队列的消费者也许等1小时才能收到第一个消息。参考官方文档发现“Only when expired messages reach the head of a queue will they actually be discarded (or dead-lettered).”只有当过期的消息到了队列的顶端(队首),才会被真正的丢弃或者进入死信队列。所以在考虑使用RabbitMQ来实现延迟任务队列的时候,需要确保业务上每个任务的延迟时间是一致的。如果遇到不同的任务类型需要不同的延时的话,需要为每一种不同延迟时间的消息建立单独的消息队列。

7.服务器频繁宕机不响应,一台nginx多台tomcat ,隔一两天就有一台甚至更多tomcat 不响应,宕机时看代码运行日志,显示最后几行日志是准备往数据库更新某行记录的状态,更新一直没结束,单表操作。直接用curl调用其他页面请求也是一直不响应。查看服务器网络状态CLOSE-WAIT两千多。数据库操作用的hibernate

  1. CLOSE_WAIT 过多主要是由于服务器被动关闭连接时,程序处理不当产生的。可以往以下几个方面进行考虑:
  2. 1.是否有资源连接没有释放
  3. 2.是否需要修改超时时间
  4. 3.是否需要修改连接数

8.工程部署运行一段时间后,占用内存越来越高,达到70~88%,jmap查看后,看到B、C、constMethodKlass等占用的内存情况,请问怎样再定位到代码层面?

  1. [C char[]
  2. [B byte[]
  3. constMethodKlass 是被 Classloader 装载的代码优化的重点是字节数组和字符数组,
  4. 分析一下代码有哪些地方用户字节数组没有回收的,使用 String 没有回收的。

9.最近踩到了Log4j的坑,开启日志后,jmeter 1000并发没事,APp端FinalHTTP、OKHttp 一人访问就很容易出现死锁。

  1. 1.先说说排查死锁的问题可以使用通用的组合排查方式:
  2. jps + jstack 以下均在windows cmd 下操作:
  3. 1jps -l :输出程序的包路径,比如,com.demo.Test
  4. 2jstack -l pid:打印 pid 对应的 Java堆栈信息,如果是死锁,你将看到如下信息:Found one Java-level deadlock
  5. 2.再说说解决死锁的问题可以归结为以下三点:
  6. 1)重新启动系统
  7. 2)撤消进程3)进程回退但是不论你怎么解决死锁的问题,对于系统来说都是要付出代价的。最好的办法就是预防死锁的发生,理清楚业务的流程、注意同步时加锁的顺序等

10.现在都说前后端分离,我是想往Java后端方向去发展,正在学ssm框架整合开发,但是开发实战经验倒是少。现在又有springboot和springcloud等技术,还有redis等NoSQL技术,现在我的疑惑是没有明确的后端学习路线?

  1. 首先要明确这些技术解决什么问题,然后才能有所侧重Spring boot 其实就是 Spring,这是 Spring Boot 简化了 Spring 项目的整合配置成本。如果你今天开始搞 spring,没有历史遗留,那就从 Spring boot 开始好了,至于怎么学习,如果英文过关,直接阅读 Spring 官网的 reference 就好,写得最详细。如果希望能有一个一步一步的引导,Spring in Action 或者 Spring Boot in Action 都是不错的书籍,但是这些书都是为了入门不够全,更多的整合需要阅读 ReferenceSpring Cloud 解决分布式系统的问题,如果你用 spring 不考虑弹性和高可用之类的问题,那就先不需要深入了。这个目前社区也有中文书了,可以搜索一下。redis Spring 结合的场景常见的有两个,一个是作为 Spring Session cache ,一个是作为普通的 KV,前者有 Spring-session 项目,后者有 Spring-data-redis 项目,可以了解一下。

11.您好,最近在阅读spring源码的时候遇到一些问题,这个框架太大了,请问下如何系统性的阅读它的源码,该怎么入手怎么阅读呢。

  1. 首先说老实话我没有系统性的阅读过,一般是碰到了问题追进去看。单单看 spring framework 最核心的依赖注入和 bean 管理应该还好,要是阅读 spring security 那就比较晦涩了。虽然我觉得阅读 spring framework 的源码的初衷是什么?如果是为了能提升自己面向对象的素养,或者提升自己对大框架的理解能力,那我觉得还是值得一读的。如果只是为了更好的熟悉 Spring framework,那我觉得不如多读几遍 referenceSpring reference 写的非常好,还是就是 spring 团队的 blog 也非常的不错。如果你觉得真的想锻炼一下自己,那我给出如下建议
  2. 一. 先把 reference 看熟悉吧,知道怎么使用,对于背后的机制有帮助
  3. 二. 设计模式先看一遍,spring 源码中的设计模式还是蛮多的,熟悉的话,看看类名就知道在干嘛了。
  4. 三. 写个 Helloworld 的例子,然后把牵扯到的 spring 背后的源码读一遍,搞清楚原理。
  5. 四. 找一个画类图的工具,复杂的类继承和依赖关系画出来,然后从核心模块开始逐步的读。我当年读 eclipse 源码的时候是这么干的,过程很辛苦,但是前提是文档资料太少,我觉得文档够多的话,其实没有必要通读,搞清楚背后原理更实用些。

12.为何字符串比较的时候要用equals,而不用==

  1. 对于引用数据类型(类、接口类型、数组类型、枚举类型、注解类型)来说,==和equals都是来比较两个变量的地址,String类重写了equals方法,所以可以比较两个字符串内容是否相等 这里判断的是字符串是否匹配,所以需要用equals方法而非“==”

13.如何解决运行java时报错:unable to load native library: libjava.jnilib

  1. .bash_profile 中设置了 DYLD_LIBRARY_PATH,可以试着取消 DYLD_LIBRARY_PATH 的设置。

14.为什么流式处理框架都是 java 写成的,JVM 是不是在流和批存在着特殊优势。还有分布式资源调度,感觉Mesos 的成长速度跟不上 Yarn。这是为什么?

  1. 1.这个和stormspark这两个鼻祖有不可分割的关系。java8开始提供了很多流和的features
  2. 2.关于 mesos yarn的比较 加你你看看这篇文章 https://www.oreilly.com/ideas/a-tale-of-two-clusters-mesos-and-yarn ,Yarn的信息量级大一级。

15.java通过jni调用c++本地库函数,c++程序崩溃,如何保障jvm不会崩?

  1. 简单的说 JNI 的调用崩溃了,就会导致 JVM Crash,因为失去了Java 那一层的保护了。我不是这方面的专家,我在社区找到了一个类似的问题,希望对你有帮助,https://stackoverflow.com/questions/44062332/jvm-crashes-when-calling-jni-function-during-gc

16.MYSQL中时间戳如何转化为标准时间?

  1. 方法有2种:
  2. 1)使用函数FROM_UNIXTIME()。SELECT FROM_UNIXTIME(时间戳的值)执行即可。
  3. 2)自己写SQL实现转换,UNIX_TIMESTAMPUnix时间戳格式从1970-01-010时刻开始,select FROM_UNIXTIME(时间戳/1000, '格式化字符串') as 标准时间from 3)也可以使用Java或者其它语言代码转换。mysql中存入的时间戳是10位的int,时间戳表示的是从1970-01-01开始经过的毫秒数。我们现在反向计算出标准时间即可。Java标准时间戳=小数点左边数据*1000+小数点右边的值 String timestamp=mysql时间戳+"000"; //放大1000long lTime = Long.parseLong(timestamp); //java标准时间戳SimpleDateFormat standardTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//转换为Java标准日期

17.java小白如何快速学会spring.io呢?

  1. 买本 spring in action 的书来看,快速了解个大概。spring.io 上有很多 spring 的衍生项目,这个要啃其官方的 reference,然后去 github 上找 spring 官方给出的 sample 项目看看。

18.java怎么高效的判断字符串是否数字(包括正负数、整数、小数)

  1. //可以用正则表达式的方式来判断,可以判断正负、整数小数Boolean isNumber = str.matches("-?[0-9]+.[0-9]");

19.对于JAVA后期应该如何进阶?

  1. Java 语言本身
  2. 1. 查漏补缺,把 SDK 里常用的方法的源码都看看。然后搞清楚每个版本都引入了那些新特性,这些新特性解决什么问题,如何使用。
  3. 2. 分布式计算,去看看 Spring cloud 相关的东西
  4. 3. Java 调优,深入到虚拟机的细节,不同的垃圾回收器的工作原理,一堆虚拟机参数该怎么配置。通用能力
  5. 4. 数据结构和算法,java 里常见的算法怎么实现的,比如 java 的排序实现,Map 的数据结构
  6. 5. 设计模式

20.JAVA中,应该怎样实现深拷贝和浅拷贝?

  1. 深拷贝(深度克隆):不仅复制对象的值类型字段,同时也复制原对象中的对象。就是说完全是新对象产生的。一种可行的方式是让类实现 Cloneable 接口,并覆盖 clone 方法。但如果类中有多个引用类型属性,这些对象有可能也会有其他的引用类型属性,那么上面这种做法就要去所有的相关类都要实现 Cloneable 接口,并覆盖 clone 方法,不仅麻烦,而且非常不利于后期维护和扩展。在Java语言里深拷贝一个对象,利用序列化来做,这是常用的方法。可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
  2. 浅拷贝:只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化;深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变。浅拷贝的方式,直接引入赋值即可。深拷贝可以利用java对象的Clone方法重写实现。
  3. 1. 深拷贝和浅拷贝 简单理解为深度拷贝和一层拷贝。区别在于浅拷贝不会拷贝嵌套属性对象只会复制引用。
  4. 2. 实现:原理如上,按照原理实现即可。
  5. 3. Java中的clone为浅拷贝。

21.Java Applet和Java Application的共同之处

  1. Java Application是何种类型,GUIWEB AppletApplication最大的不同是,一个是嵌入道浏览器执行的,一个是可以脱离浏览器执行(但两者都需要JRE的环境支持) 还有,Applet的执行方法是init(),而applicationmain函数入口(本质上,应该都是main函数入口)

《参考:https://yq.aliyun.com/search/asks/?q=》

发表评论

表情:
评论列表 (有 0 条评论,334人围观)

还没有评论,来说两句吧...

相关阅读

    相关 TechBits | HTTP

    前言 对于 HTTP 协议的了解,一直停留在它是一种通信协议,用于帮助客户端和服务端之间的传送多种类型的数据,并在客户端上进行展示。但是对于其中有些字段倒是没有过多理解,

    相关 java

    最近有个需求要把项目发布在weblogic12c服务器中,按照网上的教程对springboot项目进行改造后,陆续遇到了几个问题: 1.weblogic报错:afu.com