JUC ~~ CyclicBarrier 详解

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 个线程就可以继续向下运行了。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值