newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小
- 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
newSingleThreadPool:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务
- 如果这个唯一的线程因为异常结束,那么会有一个新的线程来接替它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行
newCacheThreadpool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程数,那么会回收部分空闲(60 秒不执行)的线程
当任务数增加时,此线程又可以智能的添加新线程来处理任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程数
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求
如何确定使用线程池的大小?
cpu 密集型通常设置 Ncpu+1,IO 密集型数量应该设置的更多
并发库中的 BlockingQueue 是一个比较好玩的类,顾名思义,就是阻塞队列。该类主要提供了两个方法 put() 和 take(),前者是将一个对象放到队列中,如果队列已经满了,就等待直到有空闲节点;后者从 head 取一个对象,如果没有对象,就等待直到有可取对象。FixedThreadPool 和 singleThreadPool 都是采用无界的 linkedBlockingQueue 实现。LinkedBlockingQueue 中引入了两把锁 takeLock 和 putLock,显然是分被用于 take 操作和 put 操作的。即 LinkedBlockingQueue 入队和出队使用的是不同的锁,那么 LinkedBlockingQueue 可以同时进行入队和出队操作。但是由于使用链表实现,所以查找速度会慢一点。
线程池对任务队列包括三种:有界队列,无界队列,同步移交
无界队列:当请求不断增加时队列将无限增加,因此会出现资源耗尽的情况
有界队列:如 LinkedBlockingQueue,ArrayBlockingQueue 等,可以避免资源耗尽的情况,但是可能会出现队列填满后应该如何处理的问题?
执行饱和策略:终止,抛出异常;抛弃:抛弃该任务;调用者运行:将任务返回给调用者。
对于非常大的队列或者无界队列,里面的任务可能会长时间排队等待。可以使用同步移交将任务直接交给工作线程执行。同步移交并不是真正的队列,只是一种在线程之间移交的策略。