CountDownLatch, CyclicBarrier, Phaser 总结

1 CountDownLatch

一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。


2 CyclicBarrier

CyclicBarrier 类有一个整数初始值,此值表示将在同一点同步的线程数量。当其中一个线程到达确定点,它会调用await() 方法来等待其他线程。当线程调用这个方法,CyclicBarrier阻塞线程进入休眠直到其他线程到达。当最后一个线程调用CyclicBarrier 类的await() 方法,它唤醒所有等待的线程并继续执行它们的任务。

注意比较CountDownLatch和CyclicBarrier:

  • CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
  • CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
  • CyclicBarrier 可以在所有线程到达但未释放时调用指定的Action (Runnable)做一些任务;


A CyclicBarrier supports an optional Runnable command that is run once per barrier point, after the last thread in the party arrives, but before any threads are released. This barrier action is useful for updating shared-state before any of the parties continue.

package test;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class CyclicBarrierTest {

	public static void main(String[] args) {
		
		List<Worker> workers = new LinkedList<Worker>();
		ResetAction ra = new ResetAction(workers);
		
		CyclicBarrier cb = new CyclicBarrier(3+1, ra);
		ra.setCyclicBarrier(cb);
		
		for(int j=0; j<5; j++){
			cb.reset();
			for(int i=0; i<3; i++){
				Worker w = new Worker(cb);
				workers.add(w);
				w.start();
			}
                        // 等待三个子线程完成
                        cb.await();
                }
		
	}
	
	public static String threadName(){
		return Thread.currentThread().getName();
	}
	
	static class Worker extends Thread {
		
		private static int WOKER_ID = 0;
		
		private CyclicBarrier cb;
		private AtomicInteger ai;
		
		public Worker(CyclicBarrier cb){
			this.cb = cb;
			this.ai = new AtomicInteger();
			setName("Worker-"+(WOKER_ID++));
		}
		
		public void run(){
			try {
				int random = new java.util.Random().nextInt(1000);
				TimeUnit.MILLISECONDS.sleep(random);
				System.out.println(threadName()+" crawling page count: "+random);
				ai.set(random);
				cb.await();
				System.out.println(threadName()+" finished work.");
			} catch (InterruptedException | BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
		
		public int getCount(){
			return ai.get();
		}
	}
	
	static class ResetAction extends Thread {
		
		private List<Worker> workers;
		private CyclicBarrier cb;
		private int generation;
		private long total;
		
		public ResetAction(List<Worker> workers){
			this.workers = workers;
			this.generation = 0;
			this.total = 0;
			setName("ResetAction");
		}
		
		public void setCyclicBarrier(CyclicBarrier cb){
			this.cb = cb;
		}
		
		public void run(){
			System.out.println("========= current generation ["+generation+"] =========");
			
			// 统计各工作线程总的工作量
			for(Worker w: workers){
				total+=w.getCount();
			}
			System.out.println("total work is: "+total+", parties: "+cb.getParties()+", waiting: "+cb.getNumberWaiting());
			if(total>10000){
				return;
			}
			
			// 重置到下一阶段
			System.out.println("========= next generation ["+(++generation)+"] =========");
			// 这里调用 reset 要抛java.util.concurrent.BrokenBarrierException
//			cb.reset();
			
		}
	}

}

3. Phaser

Phaser 其实就是CyclicBarrier的改良版,可真正的实现多次重复利用,因为要是在CyclicBarrier的 advance Action里reset的话,就抛异常了不能重复使用,但是在Phaser的onAdvance()里就可以做你向做的事,控制返回值是否为true来结束;

3.1 Registration

    Phaser支持通过register()和bulkRegister(int parties)方法来动态调整注册任务的数量,此外也支持通过其构造函数进行指定初始数量。在适当的时机,Phaser支持减少注册任务的数量,例如arriveAndDeregister()。单个Phaser实例允许的注册任务数的上限是65535。


3.2 Arrival

    正如Phaser类的名字所暗示,每个Phaser实例都会维护一个phase number,初始值为0。每当所有注册的任务都到达Phaser时,phase number累加,并在超过Integer.MAX_VALUE后清零。arrive()和arriveAndDeregister()方法用于记录到达,arriveAndAwaitAdvance()方法用于记录到达,并且等待其它未到达的任务。


3.3 Termination

    Phaser支持终止。Phaser终止之后,调用register()和bulkRegister(int parties)方法没有任何效果,arriveAndAwaitAdvance()方法也会立即返回。触发终止的时机是在protected boolean onAdvance(int phase, int registeredParties)方法返回时,如果该方法返回true,那么Phaser会被终止。默认实现是在注册任务数为0时返回true(即return registeredParties == 0;)。此外,forceTermination()方法用于强制终止,isTerminated()方法用于判断是否已经终止。


3.4 Tiering

    Phaser支持层次结构,即通过构造函数Phaser(Phaser parent)和Phaser(Phaser parent, int parties)构造一个树形结构。这有助于减轻因在单个的Phaser上注册过多的任务而导致的竞争,从而提升吞吐量,代价是增加单个操作的开销。


<pre name="code" class="java">package test;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class PhaserTest {

	public static void main(String[] args) {
		
		List<Worker> workers = new LinkedList<Worker>();
		Phaser p = new MyPhaser(workers);
		
		// register main
		p.register();
		
		for(int j=0; j<5; j++){
			// register 3 workers
			p.bulkRegister(3);
			for(int i=0; i<3; i++){
				Worker w = new Worker(p);
				workers.add(w);
				w.start();
			}
			
			System.out.println(threadName()+" current arrived: "+p.getArrivedParties());
			int arriveIndex = p.arriveAndAwaitAdvance();
			System.out.println(threadName()+" arrivate index: "+arriveIndex);
			System.out.println("workers size="+workers.size());
		}
	}
	
	public static String threadName(){
		return Thread.currentThread().getName();
	}
	
	static class Worker extends Thread {
		
		private static int WOKER_ID = 0;
		
		private Phaser p;
		private AtomicInteger ai;
		
		public Worker(Phaser p){
			this.p = p;
			this.ai = new AtomicInteger();
			setName("Worker-"+(WOKER_ID++));
		}
		
		public void run(){
			try {
				if(!p.isTerminated()){
					int random = new java.util.Random().nextInt(1000);
					TimeUnit.MILLISECONDS.sleep(random);
					System.out.println(threadName()+" crawling page count: "+random);
					ai.addAndGet(random);
					System.out.println(threadName()+" current arrived: "+p.getArrivedParties());
					int phaseNum = p.arriveAndDeregister();
					System.out.println(threadName()+" arrivate index is: "+phaseNum);
				}else{
					System.out.println("phaser has terminated");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		public int getCount(){
			return ai.get();
		}
	}
	
	static class MyPhaser extends Phaser {
		
		private List<Worker> workers;
		private long total;
		
		public MyPhaser(List<Worker> workers){
			this.workers = workers;
			this.total = 0;
		}
		
		@Override
		protected boolean onAdvance(int phase, int registeredParties) {
			System.out.println("========= current phase ["+this.getPhase()+"], p:"+phase+", r: "+registeredParties+" =========");
			
			// 统计各工作线程总的工作量
			for(Worker w: workers){
				total+=w.getCount();
			}
			System.out.println("total work is: "+total+", arrived parties: "+this.getArrivedParties()+", unarrived parties: "+this.getUnarrivedParties()+", registered: "+this.getRegisteredParties());
			workers.clear();
			
			if(total>5000){
				return true;
			}
			
			return false;
		}
	}

}

 



参考文章:

1.Java并发学习之十七——线程同步工具之CountDownLatch 

2.Java并发学习之十八——线程同步工具之CyclicBarrier

3.What's New on Java 7 Phaser


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【为什么学习这门课程?】 课程教授如何通过模式、面向对象设计技术和Java编程语言的特性来开发高质量并发软件应用程序和可重用框架。 多核的分布式核处理器、廉价的大容量存储、无处不在的连接性和通用软件平台的融合趋势,正推动着软件工程师和程序员的需求变化,他们需要知道如何为连接到云计算平台的客户端设备开发并发软件。尽管目前在处理器、存储和网络方面有许多改进,但是从客观上说,想要根据预算额度按时开发和交付高质量的软件仍然是有难度的,特别是开发高质量的并发软件应用程序和可重用服务。 【课程亮点】 本课程通过示例描述了如何通过使用面向对象的设计技术、Java编程语言特性、类库、应用模式和框架等技术要点,来有效降低并发软件开发的复杂性。课程中使用了许多Java应用程序示例来展示并发软件中的面向模式设计和编程技术。 【讲师介绍】 Douglas C. Schmidt(道格拉斯·施密特)—— ACE / TAO初始研发者、《C++网络编程》作者 施密特博士是ACE、TAO和CIAO的初始研发者,过去的20年里,领导了面向模式DRE中间件框架的发展。这些技术已被全球数千家公司和机构成功应用于许多领域,包括国防和安全、数据通信/电信、金融服务、医疗工程和大型多人在线游戏。施密特博士曾担任卡耐基梅隆大学软件工程学院的首席技术官,目前是美国范德堡大学(Vanderbilt University)的计算机科学教授。主要研究分布式实时和嵌入式系统的模式、优化、中间件和基于模型的工具。  施密特博士还是《C++报告》的前主编和《C/ C++用户》杂志专栏作家。发表了500多篇技术论文相关的话题, 主要涉及模式、优化技术、面向对象的框架和实证分析和特定领域的建模环境。与人合著了四本模式领域的经典书,包括《C++网络编程》第一卷和第二卷、《编程设计中的模式语言》、《面向模式的软件设计》。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值