Java多线程-并发工具类(二)等待多线程完成的CountDownLatch

参考:

https://www.jianshu.com/p/1716ce690637
http://ifeve.com/talk-concurrency-countdownlatch/


CountDownLatch是什么

CountDownLatch也叫闭锁,在JDK1.5被引入,允许一个或多个线程等待其他线程完成操作后再执行。

CountDownLatch内部会维护一个初始值为线程数量的计数器,主线程执行await方法,如果计数器大于0,则阻塞等待。当一个线程完成任务后,计数器值减1。当计数器为0时,表示所有的线程已经完成任务,等待的主线程被唤醒继续执行。

应用场景

应用程序的主线程希望在负责启动框架服务的线程已经完成之后再执行。在这个需求中,要实现主线程等待所有线程完成的操作,最简单的做法是使用join。但在JDK1.5之后的并发包中提供的CountDownLatch也可以实现join的这个功能,并且比join的功能更多。

例子1:

public class StartUpCheckService {
	private SimpleDateFormat sdf = new SimpleDateFormat("[YYYY-MM-dd HH:mm:ss:SSS] ");
	private void println(String msg) {
		System.out.println(sdf.format(new Date()) + msg);
	}
	
	/**
	 * 所有服务的基类,具体实现在execute方法实现
	 */
	class Service implements Runnable {
	    private CountDownLatch latch;

	    public Service(CountDownLatch latch) {
	        this.latch = latch;
	    }

	    @Override
	    public void run() {
	        try {
	            execute();
	        } finally {
	            if (latch != null)
	                latch.countDown();
	        }
	    }
	    public void execute() {}
	}
	
	/**
	 * 服务具体实现类.工作2秒
	 */
	class HealthCheckService extends Service {

	    public HealthCheckService(CountDownLatch latch) {
	        super(latch);
	    }

	    @Override
	    public void execute() {
	        try {
	        	println("HealthCheckService work...");
	            TimeUnit.SECONDS.sleep(2);
	            println("HealthCheckService done...");
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	    }
	}

	/**
	 * 服务具体实现类.工作3秒
	 */
	class DatabaseCheckService extends Service {

	    public DatabaseCheckService(CountDownLatch latch) {
	        super(latch);
	    }

	    @Override
	    public void execute() {
	        try {
	        	println("DatabaseCheckService work...");
	            TimeUnit.SECONDS.sleep(3);
	            println("DatabaseCheckService done...");
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	    }
	}
	
	/**
	 * 应用启动类,使用线程池执行每个服务的任务。负责初始化闭锁,然后等待,直到所有服务都被检测完。
	 */
	class Application {
	    private CountDownLatch latch;
	    public void startUp() throws Exception {
	        latch = new CountDownLatch(2);
	        List services = new ArrayList<>();
	        services.add(new DatabaseCheckService(latch));
	        services.add(new HealthCheckService(latch));
	        ExecutorService executor = Executors.newFixedThreadPool(services.size());
	        for (Service service : services) {
	            executor.execute(service);
	        }
	        latch.await();
	        println("all service is start up");
	        executor.shutdown();
	    }
	}

	public static void main(String arg[]) {
		try {
			new StartUpCheckService().new Application().startUp();;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

输出:

Java多线程-并发工具类(二)等待多线程完成的CountDownLatch_第1张图片

例子2:

public class Test {
	public static void main(String[] args) {
		final CountDownLatch latch = new CountDownLatch(2);

		new Thread() {
			public void run() {
				try {
					System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
					Thread.sleep(3000);
					System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
					latch.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			};
		}.start();

		new Thread() {
			public void run() {
				try {
					System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
					Thread.sleep(3000);
					System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
					latch.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			};
		}.start();

		try {
			System.out.println("等待2个子线程执行完毕...");
			latch.await();
			System.out.println("2个子线程已经执行完毕");
			System.out.println("继续执行主线程");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

输出:

子线程Thread-0正在执行
等待2个子线程执行完毕...
子线程Thread-1正在执行
子线程Thread-1执行完毕
子线程Thread-0执行完毕
2个子线程已经执行完毕
继续执行主线程

CyclicBarrier和CountDownLatch的区别

  1. CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
  2. CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。
  3. CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

你可能感兴趣的:(java,并发,多线程)