1 介绍
CountDownLatch 在解决多个线程同步方面相对于join方法己经有了不少优化,但是CountDownLatch的计数器是一次性的,也就是等到计数器值变为0后,再调用CountDownLatch的await 和countdown 方法都会立刻返回,这就起不到线程同步的效果了。所以为了满足计数器可以重置的需要,JDK开发组提供了CyclicBarrier类。可以让一组线程全部达到一个状态后再全部同时执行,执行完毕之后重置CyclicBarrier的状态可以被重用。之所以叫做屏障是因为线程调用await( )方法后就会被阻塞。所有的线程都调用了await( )后线程就会冲破屏障,继续向下执行。
2 示例代码
package com.example.demo.typeHandler.demo2;
import java.util.concurrent.*;
/**
* 旅行线程
*/
public class TravelTask implements Runnable{
private CyclicBarrier cyclicBarrier;
private String name;
private int arriveTime;//赶到的时间
public TravelTask(CyclicBarrier cyclicBarrier,String name,int arriveTime){
this.cyclicBarrier = cyclicBarrier;
this.name = name;
this.arriveTime = arriveTime;
}
@Override
public void run() {
try {
//模拟达到需要花的时间
Thread.sleep(arriveTime * 1000);
System.out.println(name +"到达集合点");
cyclicBarrier.await();
System.out.println(name +"开始旅行啦~~");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
/**
* 导游线程,都到达目的地时,发放护照和签证
*/
class TourGuideTask implements Runnable{
@Override
public void run() {
System.out.println("****导游分发护照签证****");
try {
//模拟发护照签证需要2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
*/
class Client {
public static void main(String[] args) throws Exception{
CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new TourGuideTask());
ExecutorService executor = Executors.newFixedThreadPool(3);
//登哥最大牌,到的最晚
executor.execute(new TravelTask(cyclicBarrier,"哈登",5));
executor.execute(new TravelTask(cyclicBarrier,"保罗",3));
executor.execute(new TravelTask(cyclicBarrier,"戈登",1));
executor.shutdown();
}
}
执行结果:
D:\java\jdk1.8.0_211\bin\java "-javaagent:D:\IntelliJ IDEA2017.1.2\lib\idea_rt.jar=56346:D:\IntelliJ IDEA2017.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8.0_211\jre\lib\charsets.jar;D:\java\jdk1.8.0_211\jre\lib\deploy.jar;D:\java\jdk1.8.0_211\jre\lib\ext\access-bridge-64.jar;D:\java\jdk1.8.0_211\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8.0_211\jre\lib\ext\dnsns.jar;D:\java\jdk1.8.0_211\jre\lib\ext\jaccess.jar;D:\java\jdk1.8.0_211\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8.0_211\jre\lib\ext\localedata.jar;D:\java\jdk1.8.0_211\jre\lib\ext\nashorn.jar;D:\java\jdk1.8.0_211\jre\lib\ext\sunec.jar;D:\java\jdk1.8.0_211\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8.0_211\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8.0_211\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8.0_211\jre\lib\ext\zipfs.jar;D:\java\jdk1.8.0_211\jre\lib\javaws.jar;D:\java\jdk1.8.0_211\jre\lib\jce.jar;D:\java\jdk1.8.0_211\jre\lib\jfr.jar;D:\java\jdk1.8.0_211\jre\lib\jfxswt.jar;D:\java\jdk1.8.0_211\jre\lib\jsse.jar;D:\java\jdk1.8.0_211\jre\lib\management-agent.jar;D:\java\jdk1.8.0_211\jre\lib\plugin.jar;D:\java\jdk1.8.0_211\jre\lib\resources.jar;D:\java\jdk1.8.0_211\jre\lib\rt.jar;C:\Users\wb-hll364276\IdeaProjects\demo\target\classes;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter\2.1.7.RELEASE\spring-boot-starter-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot\2.1.7.RELEASE\spring-boot-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-context\5.1.9.RELEASE\spring-context-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.7.RELEASE\spring-boot-autoconfigure-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.1.7.RELEASE\spring-boot-starter-logging-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\wb-hll364276\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;C:\Users\wb-hll364276\.m2\repository\org\slf4j\jul-to-slf4j\1.7.26\jul-to-slf4j-1.7.26.jar;C:\Users\wb-hll364276\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-core\5.1.9.RELEASE\spring-core-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-jcl\5.1.9.RELEASE\spring-jcl-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;C:\Users\wb-hll364276\.m2\repository\org\slf4j\slf4j-api\1.7.26\slf4j-api-1.7.26.jar;C:\Users\wb-hll364276\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\wb-hll364276\.m2\repository\org\projectlombok\lombok\1.16.22\lombok-1.16.22.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-test\5.0.12.RELEASE\spring-test-5.0.12.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.3.1\junit-jupiter-api-5.3.1.jar;C:\Users\wb-hll364276\.m2\repository\org\apiguardian\apiguardian-api\1.0.0\apiguardian-api-1.0.0.jar;C:\Users\wb-hll364276\.m2\repository\org\opentest4j\opentest4j\1.1.1\opentest4j-1.1.1.jar;C:\Users\wb-hll364276\.m2\repository\org\junit\platform\junit-platform-commons\1.3.2\junit-platform-commons-1.3.2.jar;C:\Users\wb-hll364276\.m2\repository\org\testng\testng\6.9.11\testng-6.9.11.jar;C:\Users\wb-hll364276\.m2\repository\org\beanshell\bsh\2.0b4\bsh-2.0b4.jar;C:\Users\wb-hll364276\.m2\repository\com\beust\jcommander\1.48\jcommander-1.48.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\ant\ant\1.7.0\ant-1.7.0.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\ant\ant-launcher\1.7.0\ant-launcher-1.7.0.jar;C:\Users\wb-hll364276\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.1.7.RELEASE\spring-boot-starter-web-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.1.7.RELEASE\spring-boot-starter-json-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.9.9\jackson-databind-2.9.9.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.9.9\jackson-core-2.9.9.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.9\jackson-datatype-jdk8-2.9.9.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.9\jackson-datatype-jsr310-2.9.9.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.9\jackson-module-parameter-names-2.9.9.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.1.7.RELEASE\spring-boot-starter-tomcat-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.22\tomcat-embed-core-9.0.22.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.22\tomcat-embed-el-9.0.22.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.22\tomcat-embed-websocket-9.0.22.jar;C:\Users\wb-hll364276\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.17.Final\hibernate-validator-6.0.17.Final.jar;C:\Users\wb-hll364276\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\wb-hll364276\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\wb-hll364276\.m2\repository\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-web\5.1.9.RELEASE\spring-web-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-beans\5.1.9.RELEASE\spring-beans-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-webmvc\5.1.9.RELEASE\spring-webmvc-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-expression\5.1.9.RELEASE\spring-expression-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\aspectj\aspectjrt\1.8.14\aspectjrt-1.8.14.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.1.7.RELEASE\spring-boot-starter-aop-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-aop\5.1.9.RELEASE\spring-aop-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\aspectj\aspectjweaver\1.8.3\aspectjweaver-1.8.3.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\httpcomponents\httpclient\4.5.3\httpclient-4.5.3.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\httpcomponents\httpcore\4.4.11\httpcore-4.4.11.jar;C:\Users\wb-hll364276\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\httpcomponents\httpmime\4.5.3\httpmime-4.5.3.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-security\2.1.7.RELEASE\spring-boot-starter-security-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\security\spring-security-config\5.1.6.RELEASE\spring-security-config-5.1.6.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\security\spring-security-core\5.1.6.RELEASE\spring-security-core-5.1.6.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\security\spring-security-web\5.1.6.RELEASE\spring-security-web-5.1.6.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\mysql\mysql-connector-java\5.1.41\mysql-connector-java-5.1.41.jar;C:\Users\wb-hll364276\.m2\repository\org\mybatis\spring\boot\mybatis-spring-boot-starter\1.3.1\mybatis-spring-boot-starter-1.3.1.jar;C:\Users\wb-hll364276\.m2\repository\org\mybatis\spring\boot\mybatis-spring-boot-autoconfigure\1.3.1\mybatis-spring-boot-autoconfigure-1.3.1.jar;C:\Users\wb-hll364276\.m2\repository\org\mybatis\mybatis\3.4.5\mybatis-3.4.5.jar;C:\Users\wb-hll364276\.m2\repository\org\mybatis\mybatis-spring\1.3.1\mybatis-spring-1.3.1.jar;C:\Users\wb-hll364276\.m2\repository\org\codehaus\groovy\groovy-all\2.4.16\groovy-all-2.4.16-indy.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-thymeleaf\2.1.7.RELEASE\spring-boot-starter-thymeleaf-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\thymeleaf\thymeleaf-spring5\3.0.11.RELEASE\thymeleaf-spring5-3.0.11.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\thymeleaf\thymeleaf\3.0.11.RELEASE\thymeleaf-3.0.11.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\attoparser\attoparser\2.0.5.RELEASE\attoparser-2.0.5.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\unbescape\unbescape\1.1.6.RELEASE\unbescape-1.1.6.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\thymeleaf\extras\thymeleaf-extras-java8time\3.0.4.RELEASE\thymeleaf-extras-java8time-3.0.4.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.1.7.RELEASE\spring-boot-starter-jdbc-2.1.7.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-jdbc\5.1.9.RELEASE\spring-jdbc-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\org\springframework\spring-tx\5.1.9.RELEASE\spring-tx-5.1.9.RELEASE.jar;C:\Users\wb-hll364276\.m2\repository\com\alibaba\fastjson\1.2.50\fastjson-1.2.50.jar;C:\Users\wb-hll364276\.m2\repository\com\google\guava\guava\23.0\guava-23.0.jar;C:\Users\wb-hll364276\.m2\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;C:\Users\wb-hll364276\.m2\repository\com\google\errorprone\error_prone_annotations\2.0.18\error_prone_annotations-2.0.18.jar;C:\Users\wb-hll364276\.m2\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;C:\Users\wb-hll364276\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;C:\Users\wb-hll364276\.m2\repository\com\hynnet\jacob\1.18\jacob-1.18.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-core\4.10.2\lucene-core-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-queryparser\4.10.2\lucene-queryparser-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-queries\4.10.2\lucene-queries-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-sandbox\4.10.2\lucene-sandbox-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-analyzers-common\4.10.2\lucene-analyzers-common-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-highlighter\4.10.2\lucene-highlighter-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\org\apache\lucene\lucene-memory\4.10.2\lucene-memory-4.10.2.jar;C:\Users\wb-hll364276\.m2\repository\com\janeluo\ikanalyzer\2012_u6\ikanalyzer-2012_u6.jar;C:\Users\wb-hll364276\.m2\repository\org\junit\platform\junit-platform-engine\1.3.2\junit-platform-engine-1.3.2.jar com.example.demo.typeHandler.demo2.Client
戈登到达集合点
保罗到达集合点
哈登到达集合点
****导游分发护照签证****
哈登开始旅行啦~~
戈登开始旅行啦~~
保罗开始旅行啦~~
有以上代码可知:在所有的线程都调用了await( )后线程就会冲破屏障,然后执行CyclicBarrier参数里的线程。
3 重点方法
3.1 await( )
await( )方法或者await(long timeout, TimeUnit unit) 内部调用的都是 doawit( )方法。下面看一下doawit( )方法。
/**
* Main barrier code, covering the various policies.
*/
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)//(2)
command.run();
ranAction = true;
nextGeneration();(3)
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out(4)
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
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 <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
- 当一个线程调用了dowait 方法后,首先会获取独占锁lock,
- 如果创建CycleBarrier 时传递的参数为10 ,那么后面9 个调用钱程会被阻塞。然后当前获取到锁的线程会对计数器count 进行递减操作,递减count=index=9 ,因为index! =O 所以当前线程会执行代码 (4)。
- 如果当前线程调用的是无参数的await( )方法,则这里timed=false ,所以当前线程会被放入条件变量的 trip 的条件阻塞队列,当前线程会被挂起并释放获取的lock锁。
- 如果调用的是有参数的await 方法则timed=true ,然后当前线程也会被放入条件变量的条件队列并释放锁资源,不同的是当前线程会在指定时间超时后自动被激活。
- 当第一个获取锁的线程由于被阻塞释放锁后,被阻塞的9 个线程中有一个会竞争到lock 锁,然后执行与第一个线程同样的操作,直到最后一个线程获取到lock 锁,
- 此时己经有9 个线程被放入了条件变量trip 的条件队列里面。最后count=index 等于0,所以执行代码(2),
- 如果创建CyclicBarrier 时传递了任务,则在其他线程被唤醒前先执行任务,任务执行完毕后再执行代码(3),唤醒其他9 个线程,并重置CyclicBarrier,然后这10 个线程就可以继续向下运行了。