同步器CountDownLatch的应用例子

本文通过一个赛跑场景的实例,详细介绍了Java并发包中的CountDownLatch类及其使用方法,包括构造、计数和等待操作,展示了如何利用CountDownLatch确保线程在所有任务完成后执行后续操作。

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

在java5.0之后jdk引入了并发包,其中提供了四种同步器来辅助线程之间的交互,CountDownLatch是其中一个,引用JDK文档中的说法就是“一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。用给定的计数初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。 ”

可以举这么一个例子,

•场景:多个队员赛跑一样,在开始的时候,裁判一声枪响,队员才能在同一个时间开始跑,但是每个队员到达终点的时间肯定是不一样的,而比赛必须在最后一个队员到达后才算结束,那么先到终点的队员就必须等待,直到最后一个队员到达,才能做下一步的事情,例如举行颁奖仪式。
package com.test.concurrent.countDownLatch;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {

	public static int count = 10;//10个选手
	public static CountDownLatch latch = new CountDownLatch(count);

	public static void play() {
		latch = new CountDownLatch(count);

		for (int i = 1; i <= count; i++) {
			new Thread(new Runner(i,latch)).start();
		}

		try {
			latch.await();//阻塞处,都得等着所有人到达终点
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("都跑到头了吧,开始颁奖。。。。啦啦啦。。。。");
	}

	public static void main(String[] args) {
		play();
	}
}

Runner类如下:

package com.test.concurrent.countDownLatch;

import java.util.concurrent.CountDownLatch;

import com.test.util.SleepUtil;

public class Runner implements Runnable {
	
	private int index;
	private CountDownLatch latch;

	public Runner(int index, CountDownLatch latch) {
		this.index = index;
		this.latch = latch;
	}

	public void run() {
		SleepUtil.sleep(1000);//可以改为随机数
		System.out.println(">>>>"+index + "号选手跑到终点!");

		latch.countDown();

	}

}

这个Demo可以运行,结果类似如下:

>>>>2号选手跑到终点!
>>>>6号选手跑到终点!
>>>>8号选手跑到终点!
>>>>10号选手跑到终点!
>>>>3号选手跑到终点!
>>>>1号选手跑到终点!
>>>>4号选手跑到终点!
>>>>5号选手跑到终点!
>>>>7号选手跑到终点!
>>>>9号选手跑到终点!
都跑到头了吧,开始颁奖。。。。啦啦啦。。。。

准备下一篇文章说一下CyclicBarrier  ^_^

<think>嗯,用户让我介绍一下同步器。我需要先理解同步器是什么,可能涉及计算机科学中的并发控制。首先,同步器应该是指在多线程环境下协调线程执行的工具或机制。常见的同步器有哪些呢?比如CountDownLatch、CyclicBarrier、Semaphore、Exchanger,可能还有Phaser这些。 用户可能需要了解这些同步器的基本功能和使用场景。得从基础讲起,先定义同步器,然后分类介绍每个的作用和例子。要注意用中文,结构清晰,按照系统级指令的要求,数学公式用$...$,独立公式用$$...$$,比如Semaphore的许可证数量可以用数学表达式表示。 要检查是否有必要用LaTeX,比如信号量的模型可以用公式展示。例如,信号量的操作可能涉及到P和V,对应的公式是申请和释放许可证的过程。不过可能用户更关注概念而不是数学细节,所以是否需要公式要看是否有必要。比如CountDownLatch的计数可以用变量表示,如初始值$n$,每次countDown()减1,await()等待直到0。 还要注意用户可能对Java中的同步器感兴趣,因为常见的同步器Javajava.util.concurrent包中。是否需要提到具体的语言实现?可能应该保持通用,但可以举例说明Java中的类。 结构方面,可以分点介绍每个同步器,说明其作用、使用场景和示例。例如: 1. CountDownLatch:等待多个任务完成,计数器递减。 2. CyclicBarrier:多个线程到达屏障后继续执行。 3. Semaphore:控制资源访问的并发数。 4. Exchanger:线程间交换数据。 5. Phaser:分阶段的任务协调。 需要确保每个部分解释清楚,例子简单易懂。同时,避免技术术语过于复杂,适合不同层次的读者。可能用户是刚开始学习多线程编程的人,所以需要从基础讲起,逐步深入。 另外,根据系统指令,行内数学表达式要用$...$,例如信号量的许可证数量可以写成$k$,或者初始值$n$。如果有公式,比如信号量的操作,可能需要写成: 当线程调用`acquire()`时,信号量内部的许可证数减1,即$S = S - 1$;若$S < 0$,则线程阻塞。调用`release()`时,许可证数加1,即$S = S + 1$。 但不确定是否需要这样的详细公式,或者是否更偏向于文字描述。根据用户的问题,可能重点在概念和应用,所以公式可能不是必须的,但可以适当加入以符合系统指令。 另外,要确保LaTeX语法正确,例如使用\int时要用$括起来。不过用户的问题可能不需要积分,但可能在例子中出现数学符号,比如初始计数器为$n$。 最后,总结同步器的作用,强调它们在多线程中的重要性,帮助协调线程执行顺序和资源共享。保持回答结构清晰,分点介绍每个同步器,使用例子帮助理解,同时符合格式要求。</think>### 同步器介绍 同步器是用于协调多线程执行顺序或共享资源访问的并发工具,其核心目标是解决线程间的**竞争条件**和**协作问题**。以下是几种典型同步器及其原理: --- #### 1. **CountDownLatch(倒计时闩)** - **作用**:主线程等待其他$n$个线程完成任务后再继续执行。 - **原理**: 内部维护一个计数器,初始值为$n$。每个线程完成任务后调用`countDown()`使计数器减1(即$n = n - 1$)。当计数器为0时,主线程通过`await()`解除阻塞。 - **示例**: ```java CountDownLatch latch = new CountDownLatch(3); // 线程完成任务后调用 latch.countDown(); latch.await(); // 主线程等待直到计数器归零 ``` --- #### 2. **CyclicBarrier(循环屏障)** - **作用**:多个线程相互等待,直到所有线程到达屏障点后再同时执行后续任务。 - **原理**: 设置屏障值$N$,每个线程调用`await()`时阻塞,直到第$N$个线程到达屏障,所有线程被唤醒。屏障可重复使用(“循环”特性)。 - **示例**: ```java CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程到达屏障")); // 每个线程执行到屏障点时调用 barrier.await(); ``` --- #### 3. **Semaphore(信号量)** - **作用**:控制同时访问共享资源的线程数量。 - **原理**: 维护许可证数量$k$。线程调用`acquire()`时,若$k > 0$则获得许可证($k = k - 1$),否则阻塞;调用`release()`时返还许可证($k = k + 1$)。 - **数学模型**: $$ S_{\text{初始}} = k, \quad \text{每次操作:} \quad S = \max(S - 1, 0) \, (\text{acquire}) \quad \text{或} \quad S = S + 1 \, (\text{release}) $$ - **示例**: ```java Semaphore semaphore = new Semaphore(3); // 允许3个线程并发访问 semaphore.acquire(); // 获取许可证 // 访问资源 semaphore.release(); // 释放许可证 ``` --- #### 4. **Exchanger(交换器)** - **作用**:两个线程在特定点交换数据。 - **原理**: 线程A调用`exchange(data1)`时阻塞,直到线程B调用`exchange(data2)`,双方交换数据并继续执行。 - **示例**: ```java Exchanger<String> exchanger = new Exchanger<>(); // 线程A: String data2 = exchanger.exchange("DataA"); // 线程B: String data1 = exchanger.exchange("DataB"); ``` --- #### 5. **Phaser(阶段器)** - **作用**:分阶段协调多线程任务,支持动态注册/注销线程。 - **原理**: 每个阶段(phase)结束时,所有线程必须到达才能进入下一阶段。通过`arriveAndAwaitAdvance()`实现同步。 - **示例**: ```java Phaser phaser = new Phaser(3); // 初始注册3个线程 phaser.arriveAndAwaitAdvance(); // 线程到达并等待其他线程 ``` --- ### 同步器的意义 通过上述机制,同步器能够: 1. **避免竞态条件**:如Semaphore限制资源访问数量。 2. **提升效率**:如CyclicBarrier协调并行计算任务。 3. **简化协作逻辑**:如CountDownLatch替代复杂的等待-通知机制。 同步器是多线程编程中不可或缺的工具,合理使用可显著提高程序的健壮性和性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值