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 workers = new LinkedList();
		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 workers;
		private CyclicBarrier cb;
		private int generation;
		private long total;
		
		public ResetAction(List 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上注册过多的任务而导致的竞争,从而提升吞吐量,代价是增加单个操作的开销。


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 workers = new LinkedList();
		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 workers;
		private long total;
		
		public MyPhaser(List 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


你可能感兴趣的:(Java)