ExecutorService 的使用

 

ExecutorService 是大神Doug Lea在并发包里为我们提供的一个便携式的线程池的操作接口,

一般使用Executors 来创建各种类型的线程池,大家比较脸熟的是下面四种
 
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
注意:上面这个创建的核心线程数是0最大线程数默认是Integer.MaxValue,高并发下创建这么多线程会对JVM造成很大的负担 很可能OOM
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
注意:上面这两个创建的队列长度默认是Integer.MaxValue,高并发下回堆积线程,造成OOM
 
这里生产环境还是不太推荐大家使用这种方式的线程池,因为它创建的线程池的都是默认配置会造成我们宝贵的资源的浪费,比如使用的DefaultThreadFactory工厂,它创建的线程都是非守护线程, ,有时候免费的东西往往套路深啊
 
部分代码

 
public static void main(String[] args) {
        //基本信息
  FutureTask uinfo = new FutureTask(()->{
           System.out.println("这里模拟uinfo调用其他API接口逻辑");
           return "user信息的结构";
  });

    //线程池
   ExecutorService executorService = Executors.newFixedThreadPool(2);
   executorService.submit(uinfo);
  System.out.println("执行结果:"+uinfo.get());
}
 
问题来了。上面的代码有没有问题?why?
......逻辑等待10S.....
答案是 肯定有问题。如果上面的代码是你的生产环境。那么你们的运维同学可能要拿刀砍你了,这个代码上线后随着时间推移随时都有可能把服务器搞崩,先从简单的现象来分析,一个java的man方法执行完毕后会打印一个日志Process finished with exit code 0,0代表正常结束 ,上面的代码你会发现,这句日志迟迟不回出现。为什么呢,结果也执行完了按理说应该一切都结束了才对呀,原因是 ExecutorService 的内部实现机制造成的。及时是main方法已经执行完毕了,但是ExecutorService 内部开启了等待加入新任务的任务。所以呢main方法虽然执行结束了但是ExecutorService 还在JVM里偷偷的跑着,在生产环境并发环境下就会创建出一个又一个的ExecutorService 对象并且还不能被回收,最后JVM只能被撑爆抛出OOM了
 
.....运维小哥哥开始换成钝刀了......
 
有人说调用.shutdown()方法就好了,没错,但是 不完美
 
扩展下,其实这里还是不够的,shudown()方法有通知的作用,就是告诉ExecutorService你可以关闭了,那什么时候关闭呢,立马关闭还是一年后关闭呢,awaitTermination(awaitTime, TimeUnit.MILLISECONDS)方法可以来设置已给结束时间,
等到了时间,使用shutdownNow()方法来彻底停掉
 
所以正确的调用顺序是:
shutdown方法
awaitTermination方法
shutdownNow方法(发生异常或者是Timeout的时候)
 
shutdown方法调用后,就不能再继续使用ExecutorService来追加新的任务了,如果继续调用execute方法执行新的任务的话,就会抛出RejectedExecutionException异常。(submit方法也会抛出上述异常)
 
awaitTermination方法也不是在它被调用的时间点上简单得等待任务结束而是在awaitTermination方法调用后,持续监视各个任务的状态以或者是否线程已经运行结束。所以不调用shutdown方法执行调用awaitTermination的话由于追加出来的任务可能会导致任务状态监视出现偏差而发生预料之外的awaitTermination的Timeout异常
 
最后贴出完整代码
public static void main(String[] args) throws InterruptedException {
   //基本信息
   FutureTask uinfo = new FutureTask(()->{
           System.out.println("这里模拟uinfo调用其他API接口逻辑");
           return "user信息的结构";
  });

     //线程池
   ExecutorService executorService = Executors.newFixedThreadPool(2);
   executorService.submit(uinfo);

  System.out.println("执行结果:"+uinfo.get());
  
   //呼叫总台可以关机
  executorService.shutdown();
   //总台:10秒后关机
   if(!executorService.awaitTermination(10, TimeUnit.SECONDS)){
          //总台:执行关机
     executorService.shutdownNow();
  }
 //加上这些代码运维同学默默的放下了手里的刀...珍爱生命,有始有终...
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章