java中四种常见线程池
Java通过Executors类提供四种线程池,我们查看源码可以知道,这四种线程池内部都是基于ThreadPoolExecutor类(Executor的子类)实现的。分别为:
-
newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
源码结构:
-
newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
源码结构:
-
newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
-
newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
-
从上面的源码可知,创建线程池都使用到了ThreadPoolExecutor类,因此为了知道每种线程池的区别,我们需要对ThreadPoolExecutor类的构造方法的参数进行解读。首先我们定位到ThreadPoolExecutor类的构造方法。
参数解读:
- corePoolSize:核心线程数(最小存活的工作线程数量);
- maximumPoolSize:最大线程数;
- keepAliveTime:线程存活时间(在corePoreSize<maximunPoolSize情况下有用,线程的空闲时间超过了keepAliveTime就会销毁);
- unit:存活时间的时间单位;
- workQueue:阻塞队列,用来保存等待被执行的任务,是一个接口对象,旗下有几个实现类为大家介绍一下:
①SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务;
②LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
③ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小。 - threadFactory:线程工厂,主要用来创建线程;
- handler:表示当拒绝处理任务时的策略,有如下几种策略:
①丢弃任务并抛出RejectedExecutionException异常;
②丢弃任务,但是不抛出异常;
③丢弃队列最前面的任务,然后重新尝试执行任务;
④由调用线程处理该任务。
- 然后我们结合第五步的参数解读就可以知道这四种线程池的创建方式。
①可缓存线程池CachedThreadPool:
根据源码可以看出:
这种线程池内部没有核心线程,线程的数量是没有限制的。
在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。
没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。
适用:执行很多短期异步的小程序或者负载较轻的服务器。
②FixedThreadPool 定长线程池:
根据源码可以看出:
该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁。
如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。
适用:执行长期的任务,性能好很多。
③SingleThreadPool 单线程线程池
根据源码可以看出:
有且仅有一个工作线程执行任务
所有任务按照指定顺序执行,即遵循队列的入队出队规则。
适用:一个任务一个任务执行的场景。【如果只是一个线程那么其实没必要使用这种线程池】
④ScheduledThreadPool 定时任务线程池
根据源码可以看出:
这个线程池有点像是CachedThreadPool和FixedThreadPool 结合了一下。
不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE。
这个线程池是上述4个中唯一一个有延迟执行和周期执行任务的线程池。
适用:周期性执行任务的场景(定期的同步数据)
- 总之,大家在实际工作中可以根据需要进行选择,采用合适的线程池来提高程序的效率。