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

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

时间: 2024-08-10 16:48:36

转发---[沧海拾遗]java并发之CountDownLatch、Semaphore和CyclicBarrier的相关文章

Java并发之CountDownLatch的使用

Java并发之CountDownLatch的使用 一. 简介 Java的并发包早在JDK5这个版本中就已经推出,而且Java的并发编程是几乎每个Java程序员都无法绕开的屏障.笔者今晚在家闲来无事,翻看了以前的博客,发现好久都没有写过博客,就想着写点东西,写点什么好了,思来想去很久,决定在这段时间里写写关于Java并发相关的东西.由于是突然兴起,所有就没有什么规划,想到什么就写点什么吧,没想到首先想到的就是CountDownLatch的这个类,那就说说这个类吧. 二. CountDownLatc

java并发之CountDownLatch、Semaphore和CyclicBarrier

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

Java并发之CountDownLatch、CyclicBarrier和Semaphore

CountDownLatch 是能使一组线程等另一组线程都跑完了再继续跑:CyclicBarrier 能够使一组线程在一个时间点上达到同步,可以是一起开始执行全部任务或者一部分任务. 这次说一下 JUC 中的同步器三个主要的成员:CountDownLatch.CyclicBarrier 和 Semaphore(不知道有没有初学者觉得这三个的名字不太好记).这三个是 JUC 中较为常用的同步器,通过它们可以方便地实现很多线程之间协作的功能.(下面的代码出自 JDK 文档) CountDownLat

Java并发之CountDownLatch

CountDownLatch是Java concurrent包下的一个同步工具.它可以让一个(或多个)线程等待,直到其他线程中的某些操作完成. 本质上是一个信号量,我们把它比作一个有N个插销的大门,它把等待(调用await)的线程挡住了, 直到其他线程把插销去完了(调用countDown减到0),这批线程才能执行. 下面是根据oracle官方文档改的一个例子: /** * * Sample usage: Here is a pair of classes in which a group of

Java 并发之AbstractQueuedSynchronizer(AQS)源码解析

关键字:CLH,Node,线程,waitStatus,CAS,中断 目录 图解AQS的操作细节 0.前言 1.基本概念 1.1.CAS自旋 1.2.Node 1.3.CLH & AQS 1.4.ReentrantLock 2.图解AQS 2.1.线程A单独运行 2.2.线程B开始运行 2.3.线程C开始运行 2.4.线程A停止运行,线程B继续运行 2.5.1.线程B停止运行,线程C继续运行 2.5.2.线程C放弃竞争 3.问题总结 3.1.为什么在unparkSuccessor操作中从尾节点开始

Java并发之Semaphore的使用

Java并发之Semaphore的使用 一.简介 今天突然发现,看着自己喜欢的球队发挥如此的棒,然后写着博客,这种感觉很爽.现在是半场时间,就趁着这个时间的空隙,说说Java并发包中另外一个重量级的类Semaphore,这个类从字面意义上理解是"信号量". 那么什么是信号量呢?我用一种比较通俗的方式来跟大家解释一下,就是在该类初始化的时候,给定一个数字A,每个线程调用acquire()方法后,首先判断A是否大于0,如果大于0,就将A减去1,然后执行对应的线程,如果不大于0,那么就会阻塞

java并发之同步辅助类CountDownLatch

CountDownLatch 含义: CountDownLatch可以理解为一个计数器在初始化时设置初始值,当一个线程需要等待某些操作先完成时,需要调用await()方法.这个方法让线程进入休眠状态直到等待的所有线程都执行完成.每调用一次countDown()方法内部计数器减1,直到计数器为0时唤醒.这个可以理解为特殊的CyclicBarrier.线程同步点比较特殊,为内部计数器值为0时开始. 方法:核心方法两个:countDown()和await()countDown():使CountDown

深入剖析java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue

关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入理解Java并发之synchronized实现原理 Java并发编程-无锁CAS与Unsafe类及其并发包Atomic 深入理解Java内存模型(JMM)及volatile关键字 剖析基于并发AQS的重入锁(ReetrantLock)及其Condition实现原理 剖析基于并发AQS的共

Java并发之CyclicBarria的使用

Java并发之CyclicBarria的使用 一.简介 笔者在写CountDownLatch这个类的时候,看到了博客园上的<浅析Java中CountDownLatch用法>这篇博文,为博主扎实的技术功底所折服,对Java多线程方面的只是信手拈来,首先在此感谢博主给了我灵感,让我进一步了解了CountDownLatch的用法,在此请收下小弟的膝盖(如果博主能够看到的化).借着<浅析Java中CountDownLatch用法>这篇博文,笔者想借着这个例子说一下 CyclicBarria