没有合适的资源?快使用搜索试试~ 我知道了~
第 10章Java并发包中线程同步器原理剖析263工 f (compareAndSetState(c, nextc))如上代码首先获取当前状态值(计数器值) 。
资源详情
资源评论
资源推荐
®
仅供才|商业用途或交流学习使用
第
10
章
Java
并发包中线程同步器原理剖析
I
263
int
nextc
=
c-1;
工 f
(compareAndSetState(c,
nextc))
return
nextc
== O;
如上代码首先获取当前状态值(计数器值)
。
代码
Cl
)判断如果当前状态值为
0
则直
接返回
fa
l
se
,从而
countDown
(
)方法直接返回:否则执行代码。)使用
CAS
将计数器
值减
1,
CAS
失败则循环重试,否则如果当前计数器值为
0
则返回
true
,返回
true
说明是
最后
一
个线程调用的
countdown
方法,那么该线程除了让计数器值减
l
外,还需要唤醒因
调用
Co
untDownLatch
的
await
方法而被阻塞的线程,具体是调用
AQS
的
doReleaseShared
方法来激活阻塞的线程。这里代码
Cl
)貌似是多余的,其实不然,之所以添加代码
Cl)
是为了防止当计数器值为
0
后,其他线程又调用了
co
untDo
wn
方法,如果没有代码
C
I ) '
状态值就可能会变成负数。
4. long
getCount
()方法
获取当前计数器的值,也就是
AQS
的
s
tate
的
值,一般在测试时使用该方法。下面看
下代码
。
publ
工
c
long
getCouηt
()
{
retur
口
sync.getCount();
int
getCount() {
return
getState
();
由如上代码可知,在其内部还是调用了
AQS
的
ge
tState
方法来获取
state
的值(计数
器当前值)。
10.1.3
小结
本节首先介绍了
Co
untDownLatch
的
使用,相比使用
jo
in
方法来实现线程间
同
步,
前者更具有灵活性和方便性
。另
外还介绍了
CountDownLatch
的原理,
CountDownLatch
是使用
AQS
实现的。使用
AQS
的状态变量来存放计数器
的值
。首先在
初始化
CountDownLatch
时设置状态值(计数器值),当多个线程调用
countdown
方法时实际是原
子性递减
AQS
的状态值。当线程调用
awa
it
方法后当前线程会被放入
AQS
的阻塞队列等
仅供非商业用途或交流
学习使
用
264
I
Java
并
发编程之美
待计数器为
0
再返
回
。其
他线 程调用
countdown
方
法让计数器值递减
l
,当计数器值变为
0
时,
当
前线程还
要调用
AQS
的
doReleaseShared
方法来激活由于调用
await
()
方法而被阻
塞
的线程
。
10.2
回环屏障
CyclicBarrier
原理探究
上节介绍的
CountDownLatch
在解决多
个
线程同步方面相对于调用线程的
join
方法己
经有了不少优
化,但
是
CountDownLatch
的
计
数器是一次性的,也就是等到计数器值变为
0
后,再调用
Co
untDownLatch
的
await
和
co
untdo
wn
方法都会立刻返回,这就起不到线
程同步的效果了
。
所以为了满足计
数器
可以
重置
的
需要,
JDK
开发组提供了
CyclicBarr
ier
类
,
并
且
CyclicBarrier
类的
功能
并不限于
CountDownLatch
的功能
。从字面意
思理解,
CyclicBarrier
是回环屏障的
意思
,它可以让
一组线程全部达到一
个状态
后再全部同
时执行
。
这里之所以叫作回环
是因为当所
有
等待线程执行完毕,并重置
CyclicBarrier
的状态
后它可
以被
重用。之所以
叫作
屏障是因为线程调用
awa
it
方法后就会被阻塞,这个阻塞点就称为
屏障点,等所有线程都调用了
aw
ait
方法后,线程
们
就会冲破屏障,继续
向
下运行。
10.
2.1
案例介绍
在介绍原理前先介绍几个实例以便加深理解。在下面的例子中,我们要实现的是,使
用两个线程去执行
一
个被分解的任
务
A
,
当
两个线
程
把自己的任务都执行完毕后再对它们
的结果进行汇总处理
。
public
class
CycleBarrierTestl
{
//
创建一个
CyclicBarrier
实例,添加
一
个所有子线程全部到达屏障后执行的任务
private
static
CyclicBarr
工
er cycl
工
cBarr
工
er=
new
CyclicBarrier
(2,
new
Runnable()
public
void
run()
{
System.out
.
println(Thread
.
currentThread()
+ ”
taskl
merge
result
”);
.
,
)
}
public
static
void
main(String[]
args)
throws
InterruptedExcept
工
on
{
//创建
一个
线程个数固定为
2
的线斗呈池
Execut
orService
executorService
=
Executors
.
newFixedThreadPoo
l(
2) ;
®
®
仅供非商业用途或交流
学习使
用
第
1
0
章
J
ava
并发包中线程同步器原理剖析
I
265
//将线程
A
添加到线程池
executorService
.
subm
工
t
(
new
Runnable
() {
public
vo
工
d
run()
{
try
{
System.out
.
println(Thread
.
currentThread(
) + ”
taskl
- 1" ) ;
System.out
.
println(Thread
.
currentThread()
+
”
enter
工
n
barrier
”
);
cyclicBarrier
.
await
();
System
.
out
.
println(Thread
.
currentThread()
+ ”
enter
out
barrier
”);
}
catch
(Exception
e)
{
e .
printStackTrace()
;
.,
)
}
//将线程
B
添加到线程池
executorService
.
submit
(new
Runnable()
{
public
void
run()
{
try
{
System
.
out
.
println(Thread
.
currentThread()
+ "
taskl
- 2
”);
System.out
.
pri 口
tln(Thread.currentThread()
+ ”
enter
in
barrier
” ) ;
cyclicBarrier
.
await
();
System
.
out
.
pri 口
tl 口(
Thread
.
currentThread{)
+ ”
enter
out
barrier
"
);
)
catch
(Except
工
on
e)
{
e .
printStackTrace()
;
.
,
)
)
//
关闭线程池
executorServ
工
ce
.
shutdown(
);
®
仅供非商业用途或交流学习使用
266
I
Ja va
并发编程之美
输出 结
果如下
。
<term
l
note
d>
Cyclo
B
ar
巾,
T
ao
tt
(Java
11pei;ca
t
lo~
生
l
b
my/Jova/JavaVJ
’
”’
IMach
l
no
叫
dkt.8
0
1
。,
j
d
k/
臼
nte
暨刷。
me/bl
叫
ova
Th
俨
e
od[pool
-
1
-
threod-1,
S
,main]
tosk1-1
Thread
[冈州-
1-thread-1,S,moin]
ente
俨
飞
n
borr
飞
er
Thr
eod[pool-1-threod-2,
S
,阳
in]
taskl
-2
Threod[pool -1-threod-2, 5
,ma
i
n]
enter
飞
n
M
俨川
e
俨
Thread
[”。
l-1
-
th
俨
ead
-
2,5,ma
i.
n]
toskl
merge
result
Thread[po
。
l-1
-
th
俨
eod-2,S,main]
ente
俨。
υt
bα
俨俨
ie
俨
Th
俨
eod[p
。。
l
-
1
-
thr
eod
-
1
,
S,moin]
enter
。
ut bo
俨俨
飞
er
如上代码创建了
一
个
Cy
clicBanier
对象,其
第一
个
参
数为计数器初始值,第
二
个参
数
R
unable
是
当
计数器
值
为
0
时需要执行的任务
。
在
ma
in
函数里面首先创建了
一
个大小
为
2
的线程池,然后添加两个子任
务
到
线
程池,
每 个 子
任务在执行
完
自己的逻辑后会调
用
awa
it
方法
。一
开始计数器值为
2
,
当第一
个线程调用
awai
t
方法时,计数器值会递减
为
1 。
由于此时计数器值不为
0
,所以
当
前线程就到了屏障点而被阻塞
。
然后第
二
个线
程调用
a
wait
时
-,会进入
屏
障,计数器值也会递减,现在计数器值为
0
,
这时就会去执行
Cy
clicBanier
构
造函数中的任务,执行完毕后退出屏障点,并且唤醒被阻塞的第
二
个线程,
这时候第
一
个线程也会退出屏障点继续向下运行
。
上面的例子说明了
多
个线程之间是相互
等
待的,假如计数器值为
N
,那么随后调用
await
方法的
N
-
1
个
线程都会因为到达
屏
障点而被阻
塞
,当第
N
个线程
i
周用
await
后,计
数器值为
0
了
,这时
候第
N
个线程才会发出通知唤醒前面的
N
I
个
线程
。
也就是当全部
线程都到达屏障点时才能
一
块继续向下执行
。
对于这个例子来说,使用
CountDownLatch
也可以得到类似的输出结果
。
下面再
举
个例子来说明
Cyc
licBaiTier
的可
复用性
。
假设
一
个任务由阶段
l
、阶段
2
和阶段
3
组成,每个线程要串行地执行阶段
1
、阶
段
2
和阶
段
3
,当多个线程执行该任务时\必须要保证所有线程的阶段
l
全部完成后才
能进入阶段
2
执行,
当所有线程的阶段
2
全部
完
成后才能进入阶段
3
执行
。
下面使用
Cy
clicBanier
来
完
成这个
需
求
。
publ
i c
class
Cyc
le
Ba
r
rierTest2
{
//
创建一个
Cyc
l
icBarr
工
er
实
例
p
riv
at
e
st
ati
c Cy
cl
icBarrier
cyc
li
cBa
rrie
r =
new
C
y
clic
Ba
rr
工 e
r
(2
);
pub
l
工
c
s t
atic
vo
工
d
main
(
String[]
arg
s)
th
r
ows
In
t
er
r
up
te
d.Exce
ption
{
ExecutorS
e
rvic
e
executo
r
Serv
i
ce
=
Executors
.
newFixedThreadPool
(2
);
®
仅
供非商业
用
途或交流学习使
用
第
10
章
Ja
v
a
并 发
包
中线程
同步
器原理剖析
I
267
//将线程
A
添
加到线程池
executorService
.
submit
(
new
Runnable(
) {
public
vo
工
d
run()
{
try
{
System
.
out
.
pr
工
ntln
(
Thread
.
currentThread(
)
+ ”
stepl
”
);
cycl
工
cBarr
工
er
.
await()
;
System.out
.
pr
工口
tln(Thread
.
currentThread()
+ ”
step2
”
);
cyclicBarrier
.
await(
);
System.out
.
println(Thread
.
currentThread()
+ ”
step3
” ) ;
}
catch
(Exception
e)
{
II
TODO
Auto
-
generated
catch
block
e.pr
工
ntStackTrace()
;
.,
)
}
//将线程
B
添加到线程池
executorService
.
submit(new
Runnable()
{
public
void
run
() {
try
{
System
.
out
.
println
(
Thread
.
currentThread(
) + ”
stepl
" ) ;
cyclicBarrier
.
await()
;
System
.
out.pr
i
ntln(Thread
.
currentThread()
+ ”
step2
”)
;
cyclicBarrier
.
await()
;
System.out.pr
工口
tl 口(
Thread
.
currentThread()
+ ”
step3
” ) ;
}
catch
(Exception
e ) {
II
TODO
Auto
ge
口
erated
catch
block
e .
printStackTrace()
;
.,
)
}
//
关闭线程池
executorService
.
shutdown
();
剩余48页未读,继续阅读
Unique先森
- 粉丝: 22
- 资源: 328
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0