记录一下CountDownLatch 和 CyclicBarrier 的使用例子

本文通过具体实例介绍了Java并发包中的CountDownLatch与CyclicBarrier组件的使用方法。CountDownLatch用于协调多个独立任务,确保所有任务完成后再继续执行后续操作;而CyclicBarrier则允许一组线程互相等待,直到到达某个公共屏障点。

之前的面试有被提到过java.util.concurrent中解决并发的问题。

看Thinking in Java 学习一下其中一些构件的使用。

本文记录关于CountDownLatch  和 CyclicBarrier 使用的小例子。

CountDownLatch 典型用法就是将一个程序分成n个相互独立的可解决的任务(锁存器)

public class CountDownLatchDemo {
	static final int SIZE = 10;
	public static void main(String[] args){
		ExecutorService exec = Executors.newCachedThreadPool();
		// All must share a single CountDownLatch object
		CountDownLatch latch = new CountDownLatch(SIZE);// 初始化CountDownLatch,并初始化计数值为100
		for (int i = 0; i < 10; i++) {
			exec.execute(new WaitingTask(latch));
		}
		for (int i = 0; i < SIZE; i++) {
			exec.execute(new TaskPortion(latch));
		}
		System.out.println("Launched all tasks");
		exec.shutdown();// Quit when all tasks complete
	}
}

注意:
  1. CountDownLatch 被设计只能触发一次,初始化时要传入一个初始计数值
  2. 初始计数值不能被重置,当任务完成时,这个锁存器调用 CountDown() 方法来减小计数值
  3. 等待被解决的任务调用 latch 的 await() 方法进入阻塞,直到正在进行的任务的计数器为 0 
上面的主函数先执行 10 个 WaitingTask(顾名思义,需要等待执行的任务),然后执行另一个任务 TaskPortion 。下面给出两个任务类的实现代码:

class TaskPortion implements Runnable {
	private static int counter = 0;
	private final int id = counter++;
	private static Random rand = new Random(47);
	private final CountDownLatch latch;
	public TaskPortion(CountDownLatch latch) {// must initialize latch in constructor because of final latch
		// TODO Auto-generated constructor stub
		this.latch = latch;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			doWork();
			latch.countDown();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void doWork() throws InterruptedException {
		// TODO Auto-generated method stub
		TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));// rand.nextInt() is safe 
		System.out.println(this + "completed");
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return String.format("%1$-3d ", id);
	}
}

注意:

  1. TaskPortion 的 doWork() 方法随机休眠一段时间来模拟部分工作的完成
  2. 当模拟任务完成后,计数值减一
  3. TaskPortion 内包含一个静态 Random 对象,有可能多线程同时调用 Random.nextInt() ,但它是线程安全的。当然可以移除 static 限定符来解决
class WaitingTask implements Runnable {
	private static int counter = 0;
	private final int id = counter++;
	private final CountDownLatch latch;
	public WaitingTask(CountDownLatch latch) {
		// TODO Auto-generated constructor stub
		this.latch = latch;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			latch.await();
			System.out.println("Latch barrier passed for " + this.toString());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			System.out.println(this + " interrupted");
		}
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return String.format("WaitingTask %1$-3d ", id);
	}
}

注意:

  1. WaitingTask 调用了 latch 的 await() 方法,会等待 TaskPortion 中计数值将为 0 在执行。
结果截图:


CyclicBarrier 可以多次重用,下一步骤在当前步骤完成前等待。

public class HorseRace {
	static final int FINISH_LINE = 10;
	private List<Horse> horses = new ArrayList<>();
	private ExecutorService exec = Executors.newCachedThreadPool();
	private CyclicBarrier barrier;
	public HorseRace(int nHorses, final int pause) {
		// TODO Auto-generated constructor stub
		barrier = new CyclicBarrier(nHorses, new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				StringBuilder s = new StringBuilder();
				for (int i = 0; i < FINISH_LINE; i++) {
					s.append("=");// The fence on the racetrack
				}
				System.out.println(s);
				for(Horse horse : horses){
					System.out.println(horse.tracks());
				}
				for(Horse horse : horses)
					if (horse.getStrides() >= FINISH_LINE) {
						System.out.println(horse + "won!");
						exec.shutdownNow();
						return;
					}
				try {
					TimeUnit.MILLISECONDS.sleep(pause);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					System.out.println("barrier-action sleep interrupted");
				}
			}
		});
		for (int i = 0; i < nHorses; i++) {
			Horse horse = new Horse(barrier);
			horses.add(horse);
			exec.execute(horse);
		}
	}
	
	public static void main(String[] args){
		int nHorses = 3;
		int pause = 200;
		if (args.length > 0) {
			int n = new Integer(args[0]);
			nHorses = n > 0 ? n : nHorses;
		}
		if (args.length > 1) {
			int p = new Integer(args[1]);
			pause = p > 0 ? p : pause;
		}
		new HorseRace(nHorses, pause);
	}
	
	
}

注意:

  1. 向 CyclicBarrier 提供了一个“栅栏动作”,即一个Runnable,以匿名内部类的形式创建;
  2. Runnable 的动作当计数值(此例中为马的数量 nHorse)到达 0 时 自动执行
  3. FINISH_LINE 定义了赛道长度,打印“=”
  4. 打印各匹马的轨迹,当有马大于等于赛道长度时,停止所有任务(shutdownNow() , 与 shutdown() 的区别)
  5. 每一个 Horse 对象都由一个线程控制,来控制进度轨迹。
class Horse implements Runnable {
	private static int counter = 0;
	private final int id = counter++;
	private int strides = 0;
	
	private static Random rand = new Random(47);
	private static CyclicBarrier barrier;
	public Horse(CyclicBarrier barrier) {
		// TODO Auto-generated constructor stub
		this.barrier = barrier;
	}
	public synchronized int getStrides(){return strides;}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			while (!Thread.interrupted()) {
				synchronized(this){
					strides += rand.nextInt(3); // Produces 0, 1, or 2
				}
				barrier.await();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			// An legitimate way to exit
			e.printStackTrace();
		} catch (BrokenBarrierException e){
			// This is the way we want to know about
			throw new RuntimeException(e);
		}
	}	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Horse " + id + " ";
	}
	public String tracks(){
		StringBuilder s = new StringBuilder();
		for (int i = 0; i < getStrides(); i++) {
			s.append("*");
		}
		s.append(id);
		return s.toString();
	}
}


我是7年经验的程序员,以下面试题请给我答案,要求全面有深度: 1.线程池核心参数有哪些?线程池工作流程?有哪几种线程池?说明不同线程池类型的使用场景及其优缺点? 2.synchronizedReentrantLock有什么区别?在实际项目中,您如何选择使用它们? 4.什么是死锁?如何预防解决死锁? 5.阻塞队列有哪些?分别介绍一下 6.在并发编程中,CountDownLatchCyclicBarrierSemaphore有何异同?请举例说明它们各自的使用场景。 7.如何通过使用volatile关键字解决Java中的可见性问题?volatile与原子操作有什么关系? 8.ConcurrentHashMap在Java1.8中相比之前的版本有哪些重要改进? 9.ConcurrentHashMap是线程安全的吗?在哪种情况下可能会出现数据一致性问题?您会如何防止这种情况发生? 10 在处理并发数据访问时,您是如何考虑选择乐观锁还是悲观锁的?为什么? 11.HashMap存储数据的过程 12.介绍一下ThreadLocal,ThreadLocal内存泄漏问题 13.谈谈你是如何理解线程安全的?有哪些实现线程安全的方案? 14.并发并行的区别? 15.synchronized的实现原理?它是如何保证原子性、可见性及有序性?锁升级的过程是怎么样的?synchronized是非公平锁吗,是如何体现非公平的? 16.如何理解CAS?存在什么问题?CAS一定要自旋吗? 17.介绍一下AQS?AQS中的同步队列条件队列原理?什么是AQS的独占模式共享模式?AQS为什么采用双向链表?
最新发布
10-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值