目录
- 准备知识:补码负数移位,停止一个线程
- 线程池的初始参数
- 线程池状态
- worker
- ctl参数
- 新增task流程
- 线程池最佳实践
补码负数移位计算
补码没问题,负数补码也没问题,但是负数(补码格式) 的移位计算把我搞晕了
有符号补码的移位运算规则:
左移右边补0,右移左边补符号位
停止一个线程
启动线程很熟悉,对停止一个线程不太熟悉
- 如果线程的任务执行完了,会自动停止
- Thread.stop方法强制停止一个线程。但是这个方法已经deprecated了。因为此方法会导致线程马上放弃已持有的锁。如果线程当时正在持有锁并执行事务操作,突然失去锁有可能导致程序的线程不安全。
- 如果一个线程是while循环的消费者,正常情况不会自动停止。可以使用thread.interrupte或者控制变量。那么在while循环操作的开头就判断线程的中段状态或者控制变量的值,判断后跳出循环,这样就可以停止线程。如果线程处于wait/sleep/join,使用interrupt会导致线程抛出异常,在处理异常的时候也可以让线程跳出循环,停止线程。
线程池的初始参数
-
corePoolSize,核心线程数。每次有新的task的时候,如果线程池的线程数 < 核心线程数,则会新建线程成为核心线程。核心线程在task执行完后不会销毁,成为while循序的消费者,一直从workQueue中取task
-
maximumPoolSize,最大线程数。有新的task,而且核心线程已经用完了,会新建线程,执行完task后销毁。
-
keepAliveTime,如果设置了核心线程不能一直alive,则线程空闲超过这个时间后,需要销毁
-
workQueue,存储不能立即处理的task的queue
-
threadFactory,线程工厂
-
handler,对于不能处理的task(线程池已经关闭或者workQueue已经满负荷),此handler处理这种情况
线程池的状态
- running,运行中
- shutdown,调用了线程池的shutdown方法。此状态不会再接收新的task,但是已有的task会继续执行
- stop,调用了线程池的shutDownNow方法。此状态不会再接收新的task,而且会取消workQueue中的task,还会中断正在执行的task(中断不能保证一定能让线程停止)
- tidying,task都已经停下来了。workQueue也空了,之后会调用terminated方法
- terminated,terminated方法执行完了
worker
Worker对象维护了一个线程和一个task的关系。Worker还实现了线程池的shutdown/stop方法(参考 “如何停止一个线程”)。
线程池中Worker的数量参考参数corePoolSize和maxiumPoolSize
ctl属性
ctl属性是一个32位的整数,前3位保存了线程池的姿态
- 111 (-1),running
- 000 (0),shutdown
- 001 (1),stop
- 010 (2),tiding
- 011 (3),terminated
后29位表示线程池中Worker的数量
新增task流程
execute方法:
- 核心线程数 < corePoolSize,增加worker,worker会去线程工厂拿一个新线程
- 1不满足,将task入workqueue
- 如果workQueue也进不去,最后再尝试新增一个worker
- 1,2,3都失败了,拒绝这个task
shutdown流程
逐一中断空闲的worker(worker是一个AbstractQueuedSynchronizer对象,如果worker在执行task之前会调用worker.lock上锁,没有锁的则表示worker是空闲的)。worker在取新task的时候也会先判断线程池状态,如果是shutdown,则会跳出循环,自动停止
shutdownNow流程
中断所有的worker,不管他是不是空闲的