-闭锁(Latch)
闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。
应用场景:
- 确保某个计算在其需要的所有资源都被初始化之后才继续执行。二元闭锁(包括两个状态)可以用来表示“资源R已经被初始化”,而所有需要R的操作都必须先在这个闭锁上等待。
- 确保某个服务在其依赖的所有其他服务都已经启动之后才启动。
- 等待直到某个操作的所有参与者都就绪在继续执行。(例如:多人游戏中需要所有玩家准备才能开始)
CountDownLatch是JDK 5+里面闭锁的一个实现,允许一个或者多个线程等待某个事件的发生。CountDownLatch有一个正数计数器,countDown方法对计数器做减操作,await方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。
-栅栏(CyclicBarrier)
栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。 栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。
场景: 应用一些协议,比如几个家庭成员决定在某个地方集合,所有人在6:00在某地集合,到了以后要等待其他人,之后才能讨论去哪里吃饭。 并行迭代,将一个问题分成很多子问题,当一系列的子问题都解决之后(所有子问题线程都已经await()),此时将栅栏打开,所有子问题线程被释放,而栅栏位置可以留着下次使用。
-例子:两个分别关于CountDownlatch和CyclicBarrier的例子
1、CountDownLatch
有三个工人在为老板干活,这个老板有一个习惯,就是当三个工人把一天的活都干完了的时候,他就来检查所有工人所干的活。记住这个条件:三个工人先全部干完活,老板才检查。所以在这里用Java代码设计两个类,Worker代表工人,Boss代表老板,具体的代码实现如下:
工人:
-
package LatchAndCyclicBarrier;
-
-
import java.util.Random;
-
import java.util.concurrent.CountDownLatch;
-
import java.util.concurrent.TimeUnit;
-
-
public
class Work implements Runnable{
-
-
-
private CountDownLatch downLatch;
-
private String name;
-
-
public
Work(CountDownLatch downLatch, String name){
-
this.downLatch = downLatch;
-
this.name = name;
-
}
-
-
public
void
run() {
-
this.doWork();
-
try{
-
TimeUnit.SECONDS.sleep(
new Random().nextInt(
10));
-
}
catch(InterruptedException ie){
-
}
-
System.out.println(
this.name +
"活干完了!");
-
this.downLatch.countDown();
-
-
}
-
-
private void doWork(){
-
System.out.println(
this.name +
"正在干活!");
-
}
-
-
}
-
-
老板:
-
-
package LatchAndCyclicBarrier;
-
-
import java.util.concurrent.CountDownLatch;
-
-
public
class Boss implements Runnable{
-
-
private CountDownLatch downLatch;
-
-
public
Boss(CountDownLatch downLatch){
-
this.downLatch = downLatch;
-
}
-
-
public
void
run() {
-
System.out.println(
"老板正在等所有的工人干完活......");
-
try {
-
this.downLatch.await();
-
}
catch (InterruptedException e) {
-
}
-
System.out.println(
"工人活都干完了,老板开始检查了!");
-
}
-
-
}
-
-
-
-
测试代码:
-
package LatchAndCyclicBarrier;
-
-
import java.util.concurrent.CountDownLatch;
-
import java.util.concurrent.ExecutorService;
-
import java.util.concurrent.Executors;
-
-
public
class TestLatch {
-
-
public
static
void
main(String[] args) {
-
ExecutorService executor = Executors.newCachedThreadPool();
-
-
CountDownLatch latch =
new CountDownLatch(
3);
-
-
Work w
1 =
new Work(latch,
"张三");
-
Work w2 =
new Work(latch,
"李四");
-
Work w3 =
new Work(latch,
"王二");
-
-
Boss boss =
new Boss(latch);
-
-
executor.execute(w3);
-
executor.execute(w2);
-
executor.execute(w1);
-
executor.execute(boss);
-
-
executor.shutdown();
-
}
-
-
}
-
-
-
-
-
执行结果:
李四正在干活! 老板正在等所有的工人干完活...... 王二正在干活! 张三正在干活! 李四活干完了! 王二活干完了! 张三活干完了! 工人活都干完了,老板开始检查了!
2、CyclicBarrier
接着上面的例子,还是这三个工人,不过这一次,这三个工人自由了,老板不用检查他们任务了,他们三个合作建桥,有三个桩,每人打一个,同时打完之后才能一起搭桥(搭桥需要三人一起合作)。也就是说三个人都打完桩之后才能继续工作。
-
-
package LatchAndCyclicBarrier;
-
-
import java.util.concurrent.CyclicBarrier;
-
-
public
class CycWork implements Runnable {
-
-
-
private CyclicBarrier cyclicBarrier ;
-
private String name ;
-
-
public
CycWork(CyclicBarrier cyclicBarrier,String name)
-
{
-
this .name =name;
-
this .cyclicBarrier =cyclicBarrier;
-
}
-
-
@Override
-
public
void
run() {
-
// TODO Auto-generated method stub
-
-
System. out .println(name +
"正在打桩,毕竟不轻松。。。。。" );
-
-
try {
-
Thread. sleep(
5000);
-
System. out .println(name +
"不容易,终于把桩打完了。。。。" );
-
cyclicBarrier .await();
-
-
}
catch (Exception e) {
-
// TODO: handle exception
-
e.printStackTrace();
-
}
-
-
System. out .println(name +
":其他逗b把桩都打完了,又得忙活了。。。" );
-
-
-
}
-
-
}
-
测试程序
-
package LatchAndCyclicBarrier;
-
-
import java.util.concurrent.CyclicBarrier;
-
import java.util.concurrent.ExecutorService;
-
import java.util.concurrent.Executors;
-
-
public
class CycTest {
-
-
public
static
void
main(String[] args)
-
{
-
ExecutorService executorpool=Executors. newFixedThreadPool(
3);
-
CyclicBarrier cyclicBarrier=
new CyclicBarrier(
3);
-
-
CycWork work
1=
new CycWork(cyclicBarrier,
"张三" );
-
CycWork work
2=
new CycWork(cyclicBarrier,
"李四" );
-
CycWork work
3=
new CycWork(cyclicBarrier,
"王五" );
-
-
executorpool.execute(work1);
-
executorpool.execute(work2);
-
executorpool.execute(work3);
-
-
executorpool.shutdown();
-
-
}
-
-
}
运行结果:
李四正在打桩,毕竟不轻松。。。。。 张三正在打桩,毕竟不轻松。。。。。 王五正在打桩,毕竟不轻松。。。。。 李四不容易,终于把桩打完了。。。。 张三不容易,终于把桩打完了。。。。 王五不容易,终于把桩打完了。。。。 王五:其他逗b把桩都打完了,又得忙活了。。。 李四:其他逗b把桩都打完了,又得忙活了。。。 张三:其他逗b把桩都打完了,又得忙活了。。。
CountDownlatch和CyclicBarrierde 源码部分
1、CountDownLatch中的两个关键方法
-
public
void
countDown() {
//对计数器减一
表示有一个事件已经发生了
-
sync.releaseShared(
1);
-
}
-
-
public
void
await()
throws
InterruptedException {
//等到计
数器为
0
-
sync.acquireSharedInterruptibly(
1);
-
}
-
await方法调用了AbstractQueuedSynchronizer中的acquireSharedInterruptibly
-
public
final
void
acquireSharedInterruptibly (
int
arg)
-
throws
InterruptedException {
-
if (Thread.interrupted())
-
throw
new InterruptedException();
-
if (tryAcquireShared(arg) <
0)
-
doAcquireSharedInterruptibly(arg);
-
}
-
public
final
boolean
releaseShared (
int
arg) {
-
if (tryReleaseShared(arg)) {
-
doReleaseShared();
-
return
true ;
-
}
-
return
false ;
-
}
-
protected
boolean
tryReleaseShared (
int
arg) {
-
throw
new UnsupportedOperationException();
-
}
-
2、CyclicBarrier中的await()方法
-
public
int
await()
throws
InterruptedException, BrokenBarrierException {
-
try {
-
return dowait(
false,
0
L);
-
}
catch (TimeoutException toe) {
-
throw
new Error(toe);
// cannot happen;
-
}
-
}
-
private
int
dowait(
boolean
timed,
long
nanos)
-
throws
InterruptedException, BrokenBarrierException,
-
TimeoutException {
-
final ReentrantLock lock =
this.lock;
-
lock.lock();
-
try {
-
final Generation g = generation;
-
-
if (g.broken)
-
throw
new BrokenBarrierException();
-
-
if (Thread.interrupted()) {
-
breakBarrier();
-
throw
new InterruptedException();
-
}
-
-
int index = --count;
-
if (index ==
0) {
// tripped
-
boolean ranAction =
false;
-
try {
-
final Runnable command = barrierCommand;
-
if (command !=
null)
-
command.run();
-
ranAction =
true;
-
nextGeneration();
-
return
0;
-
}
finally {
-
if (!ranAction)
-
breakBarrier();
-
}
-
}
-
-
// loop until tripped, broken, interrupted, or timed out
-
for (;;) {
-
try {
-
if (!timed)
-
trip.await();
-
else
if (nanos >
0
L)
-
nanos = trip.awaitNanos(nanos);
-
}
catch (InterruptedException ie) {
-
if (g == generation && ! g.broken) {
-
breakBarrier();
-
throw ie;
-
}
else {
-
// We're about to finish waiting even if we had not
-
// been interrupted, so this interrupt is deemed to
-
// "belong" to subsequent execution.
-
Thread.currentThread().interrupt();
-
}
-
}
-
-
if (g.broken)
-
throw
new BrokenBarrierException();
-
-
if (g != generation)
-
return index;
-
-
if (timed && nanos <=
0
L) {
-
breakBarrier();
-
throw
new TimeoutException();
-
}
-
}
-
}
finally {
-
lock.unlock();
-
}
-
}
-
上面dowait方法中有一个index,index=--count而count的值在源码中来自
count = parties;
提到 parties就不得不看看构造函数了
-
public
CyclicBarrier(
int
parties) {
-
this(parties,
null);
-
}
-
如上例子,我们构造了CyclicBarrier(3)那么此时的 count值为3,接着dowait源码,当index==0时,后面执行的
final Runnable command = barrierCommand;
其实是可以设置的,这个Runnable可以传进来,当我们希望所有线程都达到某一时刻之后,用什么线程执行接下来的工作,当没有传Runnable进来时,就继续执行(唤醒其他线程),否则就runnable.run()(唤醒其他线程之前执行)