![](https://app.yinxiang.com/shard/s5/res/393bb7a9-d943-42d0-b202-017861d59aed.png)
![](https://app.yinxiang.com/shard/s5/res/f1116c17-5532-4a5f-bec6-8a2e110f8011.png)
![](https://app.yinxiang.com/shard/s5/res/7e14594b-a3c0-48df-8b1c-2a737e187e99.png)
![](https://app.yinxiang.com/shard/s5/res/61d33fd8-09b4-4632-80eb-87988dc3eb02.png)
如果在一个整型
变
量上
维护
多种状
态
,就一定需要
“
按位切割使用
”
这
个
变
量,
读
写
锁
将
变
量切分成了两个部分,高
16
位表示
读
,低
16
位表示写,划分方式如
图
5-8
所示。
![](https://app.yinxiang.com/shard/s5/res/2aaeac78-4275-4cc0-867c-105234bd9d56.png)
假
设
当前同步状
态
值为
S
,写状
态
等于
S&0x0000FFFF
(将高
16
位全部抹去),
读
状
态等
于
S>>>16
(无符号
补
0
右移
16
位)。当写状
态
增加
1
时
,等于
S+1
,当
读
状
态
增加
1
时
,等于
S+(1<<16)
,也就是
S+0x00010000
。
根据状
态
的划分能得出一个推
论
:
S
不等于
0
时
,当写状
态
(
S&0x0000FFFF
)等于
0
时
,
则读
状
态
(
S>>>16
)大于
0
,即
读锁
已被
获
取。
![](https://app.yinxiang.com/shard/s5/res/b6627e1b-bded-44ac-ab70-17374d3dfe34.png)
![](https://app.yinxiang.com/shard/s5/res/f899de6b-d13d-4d0f-ab74-88f6d15fb47c.png)
![](https://app.yinxiang.com/shard/s5/res/50f5bf0f-2c4b-4b2c-8db5-d816955ff277.png)
·
添加元素
1
。
队
列更新
head
节
点的
next
节
点
为
元素
1
节
点。又因
为
tail
节
点默
认
情况下等于
head
节
点,所以它
们
的
next
节
点都指向元素
1
节
点。
·
添加元素
2
。
队
列首先
设
置元素
1
节
点的
next
节
点
为
元素
2
节
点,然后更新
tail
节
点指向元素
2
节
点。
·
添加元素
3
,
设
置
tail
节
点的
next
节
点
为
元素
3
节
点。
·
添加元素
4
,
设
置元素
3
的
next
节
点
为
元素
4
节
点,然后将
tail
节
点指向元素
4
节
点。
![](https://app.yinxiang.com/shard/s5/res/77f73f28-e7dd-418c-968b-3f65d4433848.png)
入
队
主要做两件事情:
第一是
将入
队节
点
设
置成当前
队
列尾
节
点的下一个
节
点;
第二是更新
tail
节
点,如果
tail
节
点的
next
节
点不
为
空,
则
将入
队节
点
设
置成
tail
节
点,如果
tail
节
点的
next
节
点
为
空,
则
将入
队节
点
设
置成
tail
的
next
节
点,所以
tail
节
点不
总
是尾
节
点。
出
队:
并不是每次出
队时
都更新
head
节
点,当
head
节
点里有元素
时
,直接
弹
出
head
节
点里的元素,而不会更新
head
节
点。只有当
head
节
点里没有元素
时
,出
队
操作才会更新
head
节
点。
![](https://app.yinxiang.com/shard/s5/res/e5361687-7bf5-4100-8c6a-7610648558bb.png)
首先
获
取
头节
点的元素,然后判断
头节
点元素是否
为
空,如果
为
空,表示另外一个
线
程已
经进
行了一次出
队
操作将
该节
点的元素取走,如果不
为
空,
则
使用
CAS
的方式将
头节
点的引
用设置
成
null,
如果
CAS
成功,
则
直接返回
头节
点的元素,如果不成功,表示另外一个
线
程已
经
进
行了一次出
队
操作更新了
head
节
点,
导
致元素
发
生了
变
化,需要重新
获
取
头节
点。
![](https://app.yinxiang.com/shard/s5/res/ec998fc4-3830-4cbd-89d5-e2693d761001.png)
线
程池的
处
理流程:
1
)
线
程池判断核心
线
程池里的
线
程是否都在
执
行任
务
。如果不是,
则创
建一个新的工作
线
程来
执
行任
务
。如果核心
线
程池里的
线
程都在
执
行任
务
,
则进
入下个流程。
2
)
线
程池判断工作
队
列是否已
经满
。如果工作
队
列没有
满
,
则
将新提交的任
务
存
储
在
这
个工作
队
列里。如果工作
队
列
满
了,
则进
入下个流程。
3
)
线
程池判断
线
程池的
线
程是否都
处
于工作状
态
。如果没有,
则创
建一个新的工作
线
程
来
执
行任
务
。如果已
经满
了,
则
交
给饱
和策略来
处
理
这
个任
务
。
![](https://app.yinxiang.com/shard/s5/res/a2422c84-7bca-4ee9-865d-0c1bba24f052.png)
ThreadPoolExecutor
执
行
execute
方法分下面
4
种情况:
1
)如果当前运行的
线
程少于
corePoolSize
,
则创
建新
线
程来
执
行任
务
(注意,
执
行
这
一步
骤
需要
获
取全局
锁
)。
2
)如果运行的
线
程等于或多于
corePoolSize
,
则
将任
务
加入
BlockingQueue
。
3
)如果无法将任
务
加入
BlockingQueue
(
队
列已
满
),
则创
建新的
线
程来
处
理任
务
(注意,
执
行
这
一步
骤
需要
获
取全局
锁
)。
4
)如果
创
建新
线
程将使当前运行的
线
程超出
maximumPoolSize
,任
务
将被拒
绝
,并
调
用
RejectedExecutionHandler.rejectedExecution()
方法。
![](https://app.yinxiang.com/shard/s5/res/cabb00a6-18a9-4f58-8566-486f406b2f95.png)
线
程池中的
线
程
执
行任
务
分两种情况:
1
)在
execute()
方法中
创
建一个
线
程
时
,会
让这
个
线
程
执
行当前任
务
。
2
)
这
个
线
程
执
行完
图
中
1
的任
务
后,会反复从
BlockingQueue
获
取任
务
来
执
行。
![](https://app.yinxiang.com/shard/s5/res/3a07568f-a30e-4151-910a-583272a942b2.png)
关闭线程池:
通
过调
用
线
程池的
shutdown
或
shutdownNow
方法来关
闭线
程池。
原理:
遍
历线
程池中的工作
线
程,然后逐个
调
用
线
程的
interrupt
方法来中断
线
程,所以无法响
应
中断的任
务
可能永
远
无法
终
止。
区
别:
shutdownNow
首先将
线
程池的状
态设
置成
STOP
,然后
尝试
停止所有的正在
执
行或
暂
停任
务
的
线
程,并返回等待
执
行任
务
的列表。
shutdown
只是将
线
程池的状
态设
置成
SHUTDOWN
状
态
,然后中断所有没有正在
执
行任
务
的
线
程。
只要
调
用了
这
两个关
闭
方法中的任意一个,
isShutdown
方法就会返回
true
。
当所有的任
务
都已关
闭
后,才表示
线
程池关
闭
成功,
这时调
用
isTerminaed
方法会返回
true
。
![](https://app.yinxiang.com/shard/s5/res/65282d81-4d60-426e-8e00-2652ce6aafbd.png)
FixedThreadPool:
1
)如果当前运行的
线
程数少于
corePoolSize
,
则创
建新
线
程来
执
行任
务
。
2
)在
线
程池完成
预热
之后(当前运行的
线
程数等于
corePoolSize
),将任
务
加入
LinkedBlockingQueue
。
3
)
线
程
执
行完
1
中的任
务
后,会在循
环
中反复从
LinkedBlockingQueue
获
取任
务
来
执
行。
![](https://app.yinxiang.com/shard/s5/res/4fb939dc-343b-4efc-8ac4-1e6a4c025107.png)
FixedThreadPool
使用无界
队
列
LinkedBlockingQueue
作
为线
程池的工作
队
列(
队
列的容量
为
Integer.MAX_VALUE
)。使用无界
队
列作
为
工作
队
列会
对线
程池
带
来如下影响。
1
)当
线
程池中的
线
程数达到
corePoolSize
后,新任
务
将在无界
队
列中等待,因此
线
程池中
的
线
程数不会超
过
corePoolSize
。
2
)由于
1
,使用无界
队
列
时
maximumPoolSize
将是一个无效参数。
3
)由于
1
和
2
,使用无界
队
列
时
keepAliveTime
将是一个无效参数。
4
)由于使用无界
队
列,运行中的
FixedThreadPool
(未
执
行方法
shutdown()
或
shutdownNow()
)不会拒
绝
任
务
(不会
调
用
RejectedExecutionHandler.rejectedExecution
方法)。
SingleThreadExecutor:
1
)如果当前运行的
线
程数少于
corePoolSize
(即
线
程池中无运行的
线
程),
则创
建一个新
线
程来
执
行任
务
。
2
)在
线
程池完成
预热
之后(当前
线
程池中有一个运行的
线
程),将任
务
加入
Linked-
BlockingQueue
。
3
)
线
程
执
行完
1
中的任
务
后,会在一个无限循
环
中反复从
LinkedBlockingQueue
获
取任
务
来
执
行。
![](https://app.yinxiang.com/shard/s5/res/66e09c0b-dc3d-4e9d-926f-d62cb9451b95.png)
CachedThreadPool:
1
)首先
执
行
SynchronousQueue.offer
(
Runnable task
)。如果当前
maximumPool
中有空
闲线
程
正在
执
行
SynchronousQueue.poll
(
keepAliveTime
,
TimeUnit.NANOSECONDS
),那么主
线
程
执
行
offer
操作与空
闲线
程
执
行的
poll
操作配
对
成功,主
线
程把任
务
交
给
空
线
程
执
行,
execute()
方
法
执
行完成;否
则执
行下面的步
骤
2
)。
2
)当初始
maximumPool
为
空,或者
maximumPool
中当前没有空
闲线
程
时
,将没有
线
程
执
行
SynchronousQueue.poll
(
keepAliveTime
,
TimeUnit.NANOSECONDS
)。
这
种情况下,步
骤
1
)将失
败
。此
时
CachedThreadPool
会
创
建一个新
线
程
执
行任
务
,
execute()
方法
执
行完成。
3
)在步
骤
2
)中新
创
建的
线
程将任
务执
行完后,会
执
行
SynchronousQueue.poll
(
keepAliveTime
,
TimeUnit.NANOSECONDS
)。
这
个
poll
操作会
让
空
闲线
程最多在
SynchronousQueue
中等待
60
秒
钟
。如果
60
秒
钟
内主
线
程提交了一个新任
务
(主
线
程
执
行步
骤
1
)),那么
这
个空
闲线
程将
执
行主
线
程提交的新任
务
;否
则
,
这
个空
闲线
程将
终
止。由于
空
闲
60
秒的空
闲线
程会被
终
止,因此
长时间
保持空
闲
的
CachedThreadPool
不会使用任何
资
源。
![](https://app.yinxiang.com/shard/s5/res/6528f4e9-6de2-4f1f-b41e-2fa7e1a60139.png)
SynchronousQueue
是一个没有容量的阻塞
队
列。每个插入操作必
须
等待另一
个
线
程的
对应
移除操作,反之亦然。
CachedThreadPool
使用
SynchronousQueue
,把主
线
程提交的
任
务传递给
空
闲线
程
执
行。
![](https://app.yinxiang.com/shard/s5/res/693a639f-c27b-4c98-88f6-4e824472dac0.png)
ScheduledThreadPoolExecutor:
1
)当
调
用
ScheduledThreadPoolExecutor
的
scheduleAtFixedRate()
方法或者
scheduleWith-
FixedDelay()
方法
时
,会向
ScheduledThreadPoolExecutor
的
DelayQueue
添加一个
实现
了
RunnableScheduledFuture
接口的
ScheduledFutureTask
。
2
)
线
程池中的
线
程从
DelayQueue
中
获
取
ScheduledFutureTask
,然后
执
行任
务
。
![](https://app.yinxiang.com/shard/s5/res/783e266b-8f87-4c4c-907a-bcfc1ca4a67a.png)
ScheduledThreadPoolExecutor
中的
线
程
1
执
行某个周期任
务
的
4
个步
骤:
1
)
线
程
1
从
DelayQueue
中
获
取已到期的
ScheduledFutureTask
(
DelayQueue.take()
)。到期任
务
是指
ScheduledFutureTask
的
time
大于等于当前
时间
。
2
)
线
程
1
执
行
这
个
ScheduledFutureTask
。
3
)
线
程
1
修改
ScheduledFutureTask
的
time
变
量
为
下次将要被
执
行的
时间
。
4
)
线
程
1
把
这
个修改
time
之后的
ScheduledFutureTask
放回
DelayQueue
中(
Delay-
Queue.add()
)。
![](https://app.yinxiang.com/shard/s5/res/97447ab3-49d9-4d31-b800-9a0ecd898906.png)
ScheduledThreadPoolExecutor
获
取任
务
的
过
程:
1
)
获
取
Lock
。
2
)
获
取周期任
务
。
·
如果
PriorityQueue
为
空,当前
线
程到
Condition
中等待;否
则执
行下面的
2.2
。
·
如果
PriorityQueue
的
头
元素的
time
时间
比当前
时间
大,到
Condition
中等待到
time
时间
;否
则执
行下面的
2.3
。
·
获
取
PriorityQueue
的
头
元素(
2.3.1
);如果
PriorityQueue
不
为
空,
则唤
醒在
Condition
中等待
的所有
线
程(
2.3.2
)。
3
)
释
放
Lock
。
ScheduledThreadPoolExecutor
在一个循
环
中
执
行步
骤
2
,直到
线
程从
PriorityQueue
获
取到一
个元素之后(
执
行
2.3.1
之后),才会退出无限循
环
(
结
束步
骤
2
)。
![](https://app.yinxiang.com/shard/s5/res/8430d493-67eb-48e2-8e66-c15e8ab81735.png)
ScheduledThreadPoolExecutor
添加任
务
的
过
程:
1
)
获
取
Lock
。
2
)添加任
务
。
·
向
PriorityQueue
添加任
务
。
·
如果在上面
2.1
中添加的任
务
是
PriorityQueue
的
头
元素,
唤
醒在
Condition
中等待的所有
线
程。
3
)
释
放
Lock
。
![](https://app.yinxiang.com/shard/s5/res/29483986-5cad-414b-a75e-fe905f5cbd75.png)
FutureTask:
![](https://app.yinxiang.com/shard/s5/res/f12f5036-1ffb-4ece-8561-c293769e7039.png)
当
FutureTask
处
于未启
动
或已启
动
状
态时
,
执
行
FutureTask.get()
方法将
导
致
调
用
线
程阻塞;
当
FutureTask
处
于已完成状
态时
,
执
行
FutureTask.get()
方法将
导
致
调
用
线
程立即返回
结
果或抛
出异常。
当
FutureTask
处
于未启
动
状
态时
,
执
行
FutureTask.cancel()
方法将
导
致此任
务
永
远
不会被
执
行;
当
FutureTask
处
于已启
动
状
态时
,
执
行
FutureTask.cancel
(
true
)方法将以中断
执
行此任
务线
程
的方式来
试图
停止任
务
;
当
FutureTask
处
于已启
动
状
态时
,
执
行
FutureTask.cancel
(
false
)方法将
不会
对
正在
执
行此任
务
的
线
程
产
生影响(
让
正在
执
行的任
务
运行完成);
当
FutureTask
处
于已完
成状
态时
,
执
行
FutureTask.cancel
(
…
)方法将返回
false
。
![](https://app.yinxiang.com/shard/s5/res/c17d1683-dc16-4866-97d6-4bc09c91b089.png)
假
设
开始
时
FutureTask
处
于未启
动
状
态
或已启
动
状
态
,等待
队
列中已
经
有
3
个
线
程(
A
、
B
和
C
)在等待。此
时
,
线
程
D
执
行
get()
方法将
导
致
线
程
D
也到等待
队
列中去等待。
当
线
程
E
执
行
run()
方法
时
,会
唤
醒
队
列中的第一个
线
程
A
。
线
程
A
被
唤
醒后,首先把自己从
队
列中
删
除,然后
唤
醒它的后
继线
程
B
,最后
线
程
A
从
get()
方法返回。
线
程
B
、
C
和
D
重复
A
线
程
的
处
理流程。最
终
,在
队
列中等待的所有
线
程都被
级联唤
醒并从
get()
方法返回。
![](https://app.yinxiang.com/shard/s5/res/e58945f4-13d1-4cdb-b851-cc5f49ea4d71.png)