CountDownLatch与join

本文探讨了Java中的CountDownLatch和Thread的join方法,通过实例代码展示了它们在多线程同步中的作用。CountDownLatch作为一个闭锁实现,允许主线程等待多个线程完成,而join方法则需等待线程完全执行完毕。CountDownLatch提供了更灵活的控制,可以在任务执行过程中调用countDown(),而join()则更为严格。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CountDownLatch是《java并发编程实战》这本书里讲的同步工具类的第一种,闭锁的一种实现。直接先上模拟代码:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

	public static void main(String[] args) throws InterruptedException {
		CountDownLatch countDownLatch = new CountDownLatch(2);
		FirstService t1 = new FirstService(countDownLatch);
		SecondService t2 = new SecondService(countDownLatch);
		Long start = System.currentTimeMillis();
		t1.start();
		t2.start();
		countDownLatch.await();
		Long end = System.currentTimeMillis();
		System.out.println("两个服务启动耗时:" + (end - start));
	}

}

class FirstService extends Thread {

	CountDownLatch countDownLatch;

	public FirstService(CountDownLatch countDownLatch) {
		this.countDownLatch = countDownLatch;
	}

	@Override
	public void run() {
//		synchronized (countDownLatch) {
			try {
				System.out.println("第一个服务准备启动......");
				Thread.sleep(2000);
				System.out.println("第一个服务正在启动......");
				Thread.sleep(2000);
				System.out.println("第一个服务启动完毕......");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				countDownLatch.countDown();
			}
//		}
	}
}


class SecondService extends Thread {

	CountDownLatch countDownLatch;

	public SecondService(CountDownLatch countDownLatch) {
		this.countDownLatch = countDownLatch;
	}

	@Override
	public void run() {
//		synchronized (countDownLatch) {
			try {
				System.out.println("第二个服务准备启动......");
				Thread.sleep(3000);
				System.out.println("第二个服务正在启动......");
				Thread.sleep(3000);
				System.out.println("第二个服务启动完毕......");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				countDownLatch.countDown();
			}
//		}
	}
}

这里模拟两个线程代表两个服务,main方法需要等这两个线程都执行完毕之后才进行一些操作(这里是统计服务启动时间)。所以我们使用CountDownLatch类来进行记录。
初始CountDownLatch countDownLatch = new CountDownLatch(2);创建一个值为2的CountDownLatch对象,并让两个线程共享这个对象。
主线程里调用countDownLatch.await();方法后进入阻塞,两个线程执行完毕后执行countDownLatch.countDown();方法,会将CountDownLatch里的值减1,当CountDownLatch值为0的时候,主线程的阻塞则会结束,继续运行。
await()和countDown()就是常用的两个方法。
上面代码输出如下,主线程在两个线程都执行完毕后再继续执行:

第二个服务准备启动......
第一个服务准备启动......
第一个服务正在启动......
第二个服务正在启动......
第一个服务启动完毕......
第二个服务启动完毕......
两个服务启动耗时:6005

我第一次看到这个方法的时候,感觉就效果来说跟Thread类的join()方法很像。比如我在主线程里new一个线程t1,然后调用t1.join()方法,主线程就会阻塞,直到线程t1结束后才继续进行。
所以上面这段代码,用join也能实现,分别对两个线程调用join方法就行。
但是join方法是必须等到线程完全执行完才会回到调用线程的执行逻辑,CountDownLatch则不用,你可以在线程执行的过程中就调用countDown方法,比如下面这段代码:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest2 {

	public static void main(String[] args) throws InterruptedException {
		CountDownLatch countDownLatch = new CountDownLatch(2);
		FirstService1 t1 = new FirstService1(countDownLatch);
		SecondService1 t2 = new SecondService1(countDownLatch);
		t1.start();
		t2.start();
		countDownLatch.await();
		System.out.println("两个服务第一阶段全部启动完毕");
	}

}

class FirstService1 extends Thread {

	CountDownLatch countDownLatch;

	public FirstService1(CountDownLatch countDownLatch) {
		this.countDownLatch = countDownLatch;
	}

	@Override
	public void run() {
		try {
			System.out.println("第一个服务第一阶段开始......");
			Thread.sleep(2000);
			countDownLatch.countDown();
			System.out.println("第一个服务第二阶段开始......");
			Thread.sleep(2000);
			System.out.println("第一个服务启动完毕......");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
			
		}
	}
}


class SecondService1 extends Thread {

	CountDownLatch countDownLatch;

	public SecondService1(CountDownLatch countDownLatch) {
		this.countDownLatch = countDownLatch;
	}

	@Override
	public void run() {
		try {
			System.out.println("第二个服务第一阶段开始......");
			Thread.sleep(3000);
			countDownLatch.countDown();
			System.out.println("第二个服务第二阶段开始......");
			Thread.sleep(3000);
			System.out.println("第二个服务启动完毕......");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
			
		}
	}
}

这段代码模拟两个服务,启动分两个阶段,第一阶段都执行结束之后就可以执行主线程逻辑了。于是我们把countDown方法放到线程中执行,这样在第一阶段结束后,countDownLatch对象的值就为0了,主线程的阻塞就解除了。
代码运行结果如下:

第二个服务第一阶段开始......
第一个服务第一阶段开始......
第一个服务第二阶段开始......
第二个服务第二阶段开始......
两个服务第一阶段全部启动完毕
第一个服务启动完毕......
第二个服务启动完毕......

所以使用CountDownLatch类,要比join方法灵活。

参考:
https://www.jianshu.com/p/795151ac271b
感谢大佬带飞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值