[沧海拾遗]java并发之CountDownLatch、Semaphore和CyclicBarrier

Java并发编程第9讲——CountDownLatchCyclicBarrierSemaphore(万字详解) 在JDK的并发包(JUC)里提供了几个非常有用的并发工具类。CountDownLatchCyclicBarrierSamaphore工具类提供了一种并发流程控制的手段,这同样也是面试工作中的一个重要知识点,本文将从它们的定义、常用方法、代码示例及核心源码的分析等几个要点详细介绍一下。 阅读详情

JAVA并发包中有三个类用于同步一批线程的行为,分别是CountDownLatch、Semaphore和CyclicBarrier。

CountDownLatch

CountDownLatch是一个计数器闭锁,主要的功能就是通过await()方法来阻塞住当前线程,然后等待计数器减少到0了,再唤起这些线程继续执行。 这个类里主要有两个方法,一个是向下减计数器的方法:countdown(),其实现的核心代码如下:

public boolean tryReleaseShared(int releases) {  
	// Decrement count; signal when transition to zero  
	for (;;) {   
	int c = getState();     
	if (c == 0)    
		return false;     
	int nextc = c-1;    
	if (compareAndSetState(c, nextc))    
		return nextc == 0;    
	}    
}    

很简单,如果取得当前的状态为0,说明这个锁已经结束,直接返回false;如果没有结束,然后去设置计数器减1,如果compareAndSetState不成功,则继续循环执行。 而其中的一直等待计数器归零的方法是await()。 
通过CountDownLatch可以做几件事情:

1. 主线程控制同时启动一组线程

final CountDownLatch count = new CountDownLatch(1);
for (int i = 0; i < 3; i++) {
    new Thread("Thread" + i) {
        public void run() {
            System.out.println(Thread.currentThread().getName() + " wait");
            try {
                count.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " start");
        }
    }.start();
}
//等等三秒,否则有可能3个线程并没有全部进行await状态
try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
count.countDown();

2. 主线程等待各子线程全部执行完毕后再往下执行:

final CountDownLatch count = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
    new Thread("Thread" + i) {
        public void run() {
            System.out.println(Thread.currentThread().getName() + " start");
            count.countDown();
        }
    }.start();
}
try {
    count.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("All end!!!");    

Semaphore

Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的 功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法:

  1. 用于获取权限的acquire(),其底层实现与CountDownLatch.countdown()类似;
  2. 用于释放权限的release(),其底层实现与acquire()是一个互逆的过程。

用Semaphore来实现限流代码详见:semaphore例子

CyclicBarrier

CyclicBarrier是用来一个关卡来阻挡住所有线程,等所有线程全部执行到关卡处时,再统一执行下一步操作,它里面最重要的方法是await()方法,其实现如下:

private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    //取锁,以防止在后面做减1计数时线程不安全
    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();
        }
       //如果当前线程执行到了,则将计数器减1,计数器为0则说明所有线程均执行到这里,可以调用下一步操作
       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 > 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();
    }
}    

即每个线程执行完后调用await(),然后在await()里,线程先将计数器减1,如果计数器为0,则执行定义好的操作,然后再继续执行原线程的内容。 
这个类比之前两个类的一个好处是有点类似于切面编程,可以让我们在同类线程的某个切面切入一块逻辑,并且可以同步所有的线程的执行速度。
例子代码如下:

final CyclicBarrier barrier = new CyclicBarrier(4, new Runnable() {

    @Override
    public void run() {
        System.out.println("All Threads Here");

    }
});
for (int i = 0; i < 4; i++) {
    new Thread("Thread" + i) {
        public void run() {
            System.out.println(Thread.currentThread().getName() + " wait");
            try {
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " crossed");
        }
    }.start();
}  

最终的输出结果为:

Thread0 wait
Thread1 wait 
Thread2 wait 
Thread3 wait 
All Threads Here 
Thread0 crossed 
Thread1 crossed 
Thread2 crossed 
Thread3 crossed

使用Java并发工具类:CountDownLatchCyclicBarrier的使用 CyclicBarrier内部维护一个计数器一个屏障点,当线程到达屏障点时,调用await()方法等待其他线程。- 应用场景:CyclicBarrier常用于多线程计算任务的分段执行,等待所有线程完成一段任务后再继续下一段任务。- CountDownLatch内部维护一个计数器,初始值可以设置为任意整数,每当一个线程完成操作时,计数器减1。- 其他线程在完成任务后,调用CountDownLatch的countDown()方法,将计数器减1。// 所有线程到达屏障点后执行的任务。 阅读详情

相关推荐

JAVA并发编程基础面试题及详解2

本篇文章主要是介绍了一下三个共享锁的原理,区别基本用法,底层源码希望大家能自己好好看一看,除了CyclicBarrier之外,其他不是很难,还是很有意思的。

阳谋大师的博客 260

多线程(九)并发工具

CountDownLatchCyclicBarrierSemaphore、Exchanger、Phaser

HK的博客 2428

java并发-CountDownLatchCyclicBarrier区别、简单使用demo

文章目录CountDownLatchCyclicBarrier区别 CountDownLatchCyclicBarrier区别 CountdownLatchCyclicBarrier的区别使用场景与具体实现 参考URL: https://zhuanlan.zhihu.com/p/139020914 CountDownLatchCyclicBarrier使用上的区别 参考URL: https://www.cnblogs.com/geekdc/p/11541266.html CountDownLatch

西京刀客 3290

转发---[沧海拾遗]java并发CountDownLatchSemaphoreCyclicBarrier

JAVA并发包中有三个类用于同步一批线程的行为,分别是CountDownLatchSemaphoreCyclicBarrierCountDownLatch CountDownLatch是一个计数器闭锁,主要的功能就是通过await()方法来阻塞住当前线程,然后等待计数器减少到0了,再唤起这些线程继续执行。 这个类里主要有两个方法,一个是向下减计数器的方法:countdown(),其实

Joyfulmath,一个快乐的程序员 178

Java】AQS_原理,什么是AQS 及其应用CountDownLatch (倒计时器)CyclicBarrier(循环栅栏)

AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面。AQS 是一个用来构建锁同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLock,Semaphore,其他的诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask(jdk1.7) 等等皆是基于 AQS 的。

2301_79456272的博客 1523

Java并发编程:CountDownLatchCyclicBarrierSemaphore

Java并发编程:CountDownLatchCyclicBarrierSemaphore 原文链接:   http://www.cnblogs.com/dolphin0520/p/3920397.html -------------------------...

92

Java中的并发工具类-CountDownLatchCyclicBarrier

通过上面的简单实现,我们可以看到基于计数器实现了多线程之间的门阀拦截,底层还是通过线程之间的通讯、锁计数器控制。

qq_45017958的博客 457

CountDownLatch的理解使用

在笔者想要了解Thrift时候,找到一个博主写的系统间通信技术的架构设计,在了解学习的过程中遇到很多小问题基础知识,自己还是不够清楚,就查询总结下。 因为笔者也都是从网上找的一些资料,好的资料笔者都是自己收敲一遍,这样觉得能够加深下印象,引发更多的思考,毕竟很多时候笔者感觉自己都是七秒的记忆。 在第一篇文章中遇到了一个CountDownLatch同步计数器,当计数器数值减为0时...

weixin_30951231的博客 1287

Java中的CountDownLatchCyclicBarrier有什么作用?

CountDownLatchCyclicBarrierJava并发编程中非常有用的工具,它们可以帮助我们更加方便地进行线程通信协作。CountDownLatch是一个计数器,用于等待一组线程完成执行后再执行下一步操作;而CyclicBarrier是一个屏障,用于等待一组线程达到某个屏障点后再执行下一步操作。根据不同的应用场景,我们可以选择使用CountDownLatchCyclicBarrier

it_xushixiong的博客 539

线程中run()方法start()方法区别,为什么在调用start()时,会执行run()方法

1.线程中run()方法start()方法区别 run方法是线程中的一个普通方法体,可以重复调用。是线程里的一个函数,不是多线程,直接调用run方法,相当于调用一个普通函数,执行路径还是一条, 根本没有多线程的特征。 而start方法是Thread中的线程启动方法,只能调用一次,且调用之后,线程就是就绪状态,时间片获取之后即可执行run方法中的内容。 start方法来启动一个线程,真正实现了多线程运算,调用start方法无需等待run方法运行完成,可以直接继续执行其他代码;run方法执行完毕,线程终止,然

JIESHAOCSDN的博客 1190

信号量(Semaphore)与线程计数器(CountDownLatch

本文介绍了共享锁中的信号量(Semaphore线程计数器(CountDownLatch),从各自的构造方法与常用方法出发,介绍了各自的用法及应用场景,并分别用代码实现示例进一步介绍说明他们的用法

X_H学Java的博客 2591

CountDownLatch原理解析

CountDownLatch是多线程控制的一种同步工具类,它被称为门阀、 计数器或者闭锁。这个工具经常用来用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。 它允许一个或多个线程一直等待,直到其他线程执行完后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有框架服务之后执行。

m0_38033199的博客 816

深入理解CountDownLatch原理

CountDownLatch 介绍 CountDownLatch是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作集。 CountDownLatch使用给定的计数值(count)初始化。await方法会阻塞直到当前的计数值(count)由于countDown方法的调用达到0,在这之后(即,count为0之后)所有等待的线程都会被释放,并且随后对await方法的调用都会立即返回。这是一个一次性现象 ———— count不会被重置。如果你需要一个重置count的版本,那么请考虑使用CyclicBa.

qq_33996921的博客 1622

semaphoreCountDownLatch

semaphore semaphore信号量,是并发编程的一个重要概念,表示可用资源的数量。信号量涉及的核心操作:P操作:申请一个资源(可用资源-1),V操作:释放一个资源(可用资源+1)与此同时,可用资源变化的操作均属于原子操作。 semaphore持有acquire()方法release()方法,分别对应P操作V操作。当可用资源为0时执行acquire方法时线程就会进入阻塞状态,执行release方法时就会释放资源使可用资源+1,这时被阻塞的线程将会竞争这份资源,...

qq_45780576的博客 783

SemaphoreCountDownLatch(信号量计数器)

描述SemaphoreCountDownLatch的区别

qq_42763903的博客 812

SemaphoreCountDownLatch详解

而调用await()方法时,当前线程就会判断state属性是否为0,如果为0,则继续往下执行,如果不为0,则使当前线程进入等待状态,直到某个线程将state属性置为0,其就会唤醒在 await()方法中等待的线程。Semaphore,俗称信号量,他是操作系统中PV操作原语在Java层面的实现,它也是基于AQS实现的。当幻想之后下一个线程就可以被唤醒执行后面的逻辑,假设被唤醒的线程可以获取到资源 ,它又会重新进入doAcquireSharedInterruptibly的for循环执行上面的获取资源的流程。

qq_43456605的博客 1506

Java并发中的CountDownLatchCyclicBarrier以及Semaphore

CountDownLatch 类是位于 java.util.concurrent 包下的,是一个同步工具类,允许一个或多个线程一起等待其他线程的操作执行完成后再执行相关操作。CountDownLatch 是基于线程计数器来实现并发访问控制的,主要用于主线程等待其他其他子线程都执行完毕后执行相关操作。 其使用过程为:在主线程中定义 CountDownLatch, 并将线程计数器的初始值设置为子线程的个数,多个子线程并发的执行,每个子线程在执行完毕后都会调用 countDown 函数将计数器的值减 1,直到线程

古城的博客 835
上一篇: [沧海拾遗]读<Linux/Unix设计思想>个人所思
下一篇: [沧海拾遗]java并发之Executor
yanhandle
博客等级 码龄15年 19粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值