一、线程池中比较重要的几个类:
(1)ExecutorService:线程池接口;
(2)ScheduledExecutorService:能和Timer/TimerTask类似,解决那些需要任务重复执行的问题;
(3)ThreadPoolExecutor:ExecutorService的默认实现;
(4)ScheduledThreadPoolExecutor:继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
另外,Executor是线程池的顶级接口,并不是真正意义上的线程池;Executors是一个创建线程池的工具类;
二、创建线程池有以下几种方式:
1、通过Executors创建:
(1)创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行:
Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("test").build());
(2)创建一个定长线程池,支持定时及周期性任务执行:
Executors.newScheduledThreadPool(5, new ThreadFactoryBuilder().setNameFormat("scheduledPool").build());
(3)创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 不限线程数,任何提交的任务都将立即执行:
Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("cachedPool").build());
(4)创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
Executors.newFixedThreadPool(5, new ThreadFactoryBuilder().setNameFormat("fixedPool").build());
通过Executors创建线程池虽然简单,但是也有安全隐患,由于其创建线程池时并没有指定工作队列的长度,所以工作队列可以是无限长的,这就可能会在等待队列过长时,发生OOM异常。所以尽量不要使用这种方法创建,在阿里编码规范中严格禁止使用这种方式。
2、通过自定义线程池:
(1)通过ThreadPoolExcutor创建线程池:
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), new ThreadFactoryBuilder().setNameFormat("myThreadPool").build(), new ThreadPoolExecutor.DiscardPolicy()); //一个定长的线程池
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(3, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("myThread").build(), new ThreadPoolExecutor.DiscardPolicy()); //一个可缓存的线程池
ThreadPoolExecutor executor2 = new ThreadPoolExecutor(1,1,0,TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("myThrad").build(), new ThreadPoolExecutor.DiscardPolicy()); //一个单一线程池
(2)通过ScheduledThreadPoolExecutor创建线程池:
ThreadPoolExecutor executor3 = new ScheduledThreadPoolExecutor(3, new ThreadFactoryBuilder().setNameFormat("myThread").build()); //定长,且支持周期任务和定时的线程池
线程池中用到的WorkingQueue类型有以下四种:
(1)SynchronousQueue:(同步队列)这个队列接收到任务的时候,会直接提交给线程处理,而不保留它(名字定义为 同步队列)。但有一种情况,假设所有线程都在工作怎么办?
这种情况下,SynchronousQueue就会新建一个线程来处理这个任务。所以为了保证不出现(线程数达到了maximumPoolSize而不能新建线程)的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大,去规避这个使用风险;
(2)LinkedBlockingQueue(链表阻塞队列):这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize;
(3)ArrayBlockingQueue(数组阻塞队列):可以限定队列的长度(既然是数组,那么就限定了大小),接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误;
(4)DelayQueue(延迟队列):队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。
RejectedExecutionHandlar线程池四种拒绝任务策略:
(1)直接丢弃(DiscardPolicy);
(2)丢弃队列中最老的任务(DiscardOldestPolicy);
(3)抛异常(AbortPolicy);
(4)将任务交由调用线程来执行(CallerRunsPolicy)。
这四种策略是独立无关的,是对任务拒绝处理的四中表现形式。最简单的方式就是直接丢弃任务。但是却有两种方式,到底是该丢弃哪一个任务,比如可以丢弃当前将要加入队列的任务本身(DiscardPolicy)或者丢弃任务队列中最旧任务(DiscardOldestPolicy)。丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理。除了丢弃任务还可以直接抛出一个异常(RejectedExecutionException),这是比较简单的方式。抛出异常的方式(AbortPolicy)尽管实现方式比较简单,但是由于抛出一个RuntimeException,因此会中断调用者的处理过程。除了抛出异常以外还可以不进入线程池执行,在这种方式(CallerRunsPolicy)中任务将有调用者线程去执行。
全部代码:
Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("test").build());
Executors.newScheduledThreadPool(5, new ThreadFactoryBuilder().setNameFormat("scheduledPool").build());
Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("cachedPool").build());
Executors.newFixedThreadPool(5, new ThreadFactoryBuilder().setNameFormat("fixedPool").build());
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), new ThreadFactoryBuilder().setNameFormat("myThreadPool").build());
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(3, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("myThread").build());
ThreadPoolExecutor executor2 = new ThreadPoolExecutor(1,1,0,TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("myThrad").build());
ThreadPoolExecutor executor3 = new ScheduledThreadPoolExecutor(3, new ThreadFactoryBuilder().setNameFormat("myThread").build());
参考文档:
(1)https://blog.csdn.net/achuo/article/details/80623893
(2)作者:骑小猪看流星
链接:https://www.jianshu.com/p/50fffbf21b39
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。