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();
			}
		}
	}
}

運行結果:

相關文章
相關標籤/搜索