Java并发编程:CountDownLatch的使用以及一个容易踩到的陷阱

使用简介

CountDownLatch是经过一个计数器来实现的,当咱们在new 一个CountDownLatch对象的时候须要带入该计数器值,该值就表示了线程的数量。每当一个线程完成本身的任务后,计数器的值就会减1。当计数器的值变为0时,就表示全部的线程均已经完成了任务,而后就能够恢复等待的线程继续执行了。java

CountDownLatch所描述的是”在完成一组正在其余线程中执行的操做以前,它容许一个或多个线程一直等待“。在API中是这样描述的:ide

用给定的计数 初始化 CountDownLatch。因为调用了 countDown() 方法,因此在当前计数到达零以前,await 方法会一直受阻塞。以后,会释放全部等待的线程,await 的全部后续调用都将当即返回。这种现象只出现一次——计数没法被重置。若是须要重置计数,请考虑使用 CyclicBarrier。spa

 

应用示例

示例使用开会案例。老板进入会议室等待5我的所有到达会议室才会开会。因此这里有两个线程老板等待开会线程、员工到达会议室,下面这段程序有惊天陷阱,请勿copy!:线程

陷阱程序:

package com.chenjun.testxxx;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
	private static CountDownLatch countDownLatch = new CountDownLatch(5);

	static class BossThread extends Thread {
		@Override
		public void run() {
			System.out.println("Boss在会议室等待,总共有" + countDownLatch.getCount() + "我的开会...");
			try {
				// Boss等待
				countDownLatch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			System.out.println("全部人都已经到齐了,开会吧...");
		}

		// 员工到达会议室
		static class EmpleoyeeThread extends Thread {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getName() + ",到达会议室....");
				}finally {
					// 员工到达会议室 count - 1
					countDownLatch.countDown();
				}
			}
		}

		public static void main(String[] args) {
			// Boss线程启动
			new BossThread().start();

			for (long i = 0; i < countDownLatch.getCount(); i++) {
				new EmpleoyeeThread().start();
			}

		}
	}
}

运行这段程序发现常常程序没法结束,死等在原地, 缘由就出如今main里面的for循环那一行,countDownLatch.getCount()是个会变的东西, 用来放在for循环里面,会影响循环计数,这是个很隐秘的陷阱!code

改造后的程序:

package com.chenjun.testxxx;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
	private static CountDownLatch countDownLatch = new CountDownLatch(5);

	static class BossThread extends Thread {
		@Override
		public void run() {
			System.out.println("Boss在会议室等待,总共有" + countDownLatch.getCount() + "我的开会...");
			try {
				// Boss等待
				countDownLatch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			System.out.println("全部人都已经到齐了,开会吧...");
		}

		// 员工到达会议室
		static class EmpleoyeeThread extends Thread {
			@Override
			public void run() {
				try {
					System.out.println(Thread.currentThread().getName() + ",到达会议室....");
				}finally {
					// 员工到达会议室 count - 1
					countDownLatch.countDown();
				}
			}
		}

		public static void main(String[] args) {
			// Boss线程启动
			new BossThread().start();
			
			long cnt = countDownLatch.getCount();
			
			for (long i = 0; i < cnt; i++) {
				new EmpleoyeeThread().start();
			}
		}
	}
}

运行结果:

相关文章
相关标签/搜索