1. ThreadPoolExecutor 就是一个帮你管理线程的工具,让你脱离自己启动线程管理线程的烦恼。
2. ThreadPoolExecutor 部分与生产者消费者模式类似。
可以做如下抽象:
产品:用户需要用来多线程运行的类,该类一般继承Runnable/Callable接口
生产者:ThreadPoolExecutor.submit
消费者:ThreadPoolExecutor.Worker
同步队列:BlockingQueue
则有如下现象:
1. 在没有超出corePoolSize之前每来一个产品,都会新增一个消费者。新增的消费者会首先消费新增时候自带的产品,消费完成以后就会处于阻塞状态。
2. 在超出corePoolSize之后来的每个产品,都会塞入同步队列中,直到同步队列排满。同步队列中的产品会被处于阻塞状态的消费者进行消费。
3. 如果同步队列排满则继续新增消费者,直到消费者数目大于maximumPoolSize。此时新增的消费者属于有时间限制的消费者,如果处于阻塞状态过长(当同步队列所有产品都消费完并且没有产品再过来了)则自动死亡。
4. 如果消费者总数量大于maximumPoolSize则可以交给RejectedExecutionHandler处理:
a. 直接抛出Reject exception
b. 直接忽略该runnable,不可取
c. 丢弃最早入队列的的任务
d. 直接让原先的client thread做为worker线程,进行执行
有如下总结:
1. 每个消费者出生都会自带一个产品,当消费完成之后就会眼巴巴的等待同步队列中的产品添加。
2. 这里的消费者虽然是线程池的概念,但是新来的产品并不会直接找到空闲的线程,而是要么新建消费者要么向同步队列中添加。
3. 消费者的数量如果曾经达到过corePoolSize,那么消费者永远不会少于corePoolSize数量的。
4. 在消费者数量大于corePoolSize且同步队列没有排满的时候,整个实现就是生产者消费者模式:新增的产品直接丢入同步队列,消费者直接从同步队列中获取产品。
5. ThreadPoolExecutor将生产者消费者模式进行了升级,从而实现:既满足仅仅创建一两个线程没必要使用线程池的时候,能快速创建线程。又满足了多线程情况下使用线程池来优化管理。
使用情况:
a. corePoolSize = 0: 则没有核心线程保持运行状态,适用于运行频率比较低的情况。
b. 同步队列选择无界:适用于任务量不大的情况,不然容易OOM
c. 同步队列选择有界:需要选择合适的方式处理RejectedExecutionHandler
d.同步队列block size>corePoolSize: 因为如果block size<corePoolSize则会出现线程池的浪费。
e.maximumPoolSize>=corePoolSize: 必然啊