控制线程间顺序-CountDownLatch,CylicBarrier,Semaphore
控制线程间顺序
有A,B.C三个线程,如何保证三个线程同时执行? 如何在并发情况下保证三个线程依次执行? 如何保证三个线程有序交错进行?
简单来说:
主要就是利用并发工具解决,通常有三个常用的并发工具:CountDownLatch,CylicBarrier,Semaphore
CountDownLatch
一组线程(一个或者多个)等待,一直堵塞,直到计数器变为0,所有线程唤醒同时进行。
CylicBarrier
多个线程间相互协作,每次到达一个栅栏点同时出发,计数器可循环使用,每次到达一个栅栏点就会重置计数器,可用于下一轮同步操作。
Semaphore
信号量,比如信号量10个,也就相当于共享资源,每个线程需要多少个信号量,线程执行完了才会释放它所占用的信号量。
同时进行,可以利用CountDownLatch或CylicBarrier,让三个线程都调用他们的await方法,等待同时启动。
依次执行,可以定义可见的(volatile)一个变量,每个线程判断是否是为自己信号量的值,是的话执行,执行完了赋值为下一个线程信号量的值。(不用循环可以直接赋值完,return结束该线程)
也可以T3调用join方法,加入T2,T2调用join方法,加入T1,。
有序交错执行,可以定义三个信号量,其中有acquire方法获取信号量,每个线程不断循环,执行完就释放下一个线程的信号量,使用release方法
CountDownLatch
、CyclicBarrier
和 Semaphore
都是 Java 中用于并发编程的工具类,它们都可以协调多个线程之间的执行顺序或控制对共享资源的访问,但在功能和使用场景上有所不同,以下是详细介绍:
CountDownLatch(倒计时门闩)
1. 概念与原理
CountDownLatch
允许一个或多个线程等待其他线程完成操作。它内部维护了一个计数器,当一个线程完成了自己的任务后,调用 countDown
方法会使计数器减 1。其他线程可以调用 await
方法等待计数器变为 0,此时表示所有依赖的任务都已完成,等待的线程可以继续执行后续操作。
2. 使用场景
-
等待多个线程完成任务:例如,在一个并行计算任务中,主线程需要等待多个子线程完成计算后才能合并结果。假设有 4 个子线程负责计算不同部分的数据,主线程可以创建一个初始值为 4 的
CountDownLatch
,每个子线程在完成计算后调用countDown
,主线程调用await
等待计数器变为 0 后再进行结果合并。 -
初始化资源后再执行其他操作:在应用程序启动时,可能需要初始化一些共享资源,如数据库连接池、缓存等,这些初始化操作可以由多个线程并行完成,主线程使用
CountDownLatch
等待所有初始化线程完成后再启动其他业务逻辑。
CyclicBarrier(循环栅栏)
1. 概念与原理
CyclicBarrier
允许一组线程相互等待,直到所有线程都到达一个公共的栅栏点(barrier point)后再一起继续执行后续操作。它内部维护了一个计数器,当线程调用 await
方法时,计数器会减 1,当计数器减到 0 时,表示所有线程都已到达栅栏点,此时所有等待的线程会被释放,继续执行后续操作。并且 CyclicBarrier
可以在所有线程到达栅栏点后执行一个预定义的动作(通过构造函数传入一个 Runnable
实现)。
2. 使用场景
-
多线程协作完成阶段性任务:比如在一个复杂的计算任务中,需要多个步骤,每个步骤由不同的线程组执行,每个线程组内部的线程之间需要相互等待完成当前步骤后再一起进入下一个步骤。例如,在一个数据分析任务中,第一步可能需要多个线程读取不同数据源的数据,第二步需要对这些数据进行合并和预处理,每个步骤可以使用一个
CyclicBarrier
来确保线程之间的协作。 -
模拟并发测试场景:在进行并发测试时,可以使用
CyclicBarrier
让多个模拟客户端线程同时发起请求,以便测试系统在高并发情况下的性能和稳定性。
Semaphore(信号量)
1. 概念与原理
Semaphore
用于控制对共享资源的访问数量,它维护了一个许可集(permit set)。线程在访问共享资源之前需要获取许可(通过调用 acquire
方法),如果许可数量足够,线程获取许可后可以继续执行访问共享资源的操作;如果许可数量不足,线程会被阻塞,直到有其他线程释放许可(通过调用 release
方法)。
2. 使用场景
-
控制资源访问并发数:例如,在一个服务器应用中,限制同时访问某个特定资源(如文件、数据库连接等)的线程数量。假设有一个数据库连接池,只允许 10 个线程同时获取连接进行数据库操作,可以创建一个初始许可数量为 10 的
Semaphore
,每个线程在获取数据库连接前先获取Semaphore
的许可,使用完连接后释放许可,这样就可以确保同时访问数据库连接的线程数量不超过 10 个,避免资源过度占用和竞争。 -
实现简单的资源池:可以使用
Semaphore
来构建一个简单的对象池,如线程池、连接池等。当对象池中有空闲对象时,线程可以获取许可并从池中取出对象使用,使用完毕后归还对象并释放许可,以便其他线程可以获取使用。
三者的区别
1. 功能侧重点
-
CountDownLatch
侧重于让一个或多个线程等待其他线程完成任务,强调的是任务之间的先后顺序依赖关系。 -
CyclicBarrier
侧重于多个线程之间的相互等待,实现多个线程在某个点同步,并且可以在同步点执行额外的动作,更强调线程之间的协作。 -
Semaphore
侧重于控制对共享资源的并发访问数量,限制在同一时刻能够访问共享资源的线程数量,主要用于资源管理和保护。
2. 计数器行为
-
CountDownLatch
的计数器只能使用一次,一旦计数器变为 0,就不能再被重置。 -
CyclicBarrier
的计数器可以循环使用,当所有线程到达栅栏点后,计数器会被重置,可以再次用于下一轮的线程同步。 -
Semaphore
的许可数量可以通过构造函数进行初始化,并且在运行过程中可以动态地增加或减少许可数量(通过release
和acquire
方法的不同调用方式)。
3. 线程等待与唤醒机制
-
CountDownLatch
中等待的线程会一直阻塞,直到计数器变为 0 或者等待线程被中断。当计数器变为 0 时,所有等待的线程会被唤醒,继续执行后续操作。 -
CyclicBarrier
中等待的线程也会阻塞,直到所有线程都到达栅栏点(计数器变为 0),此时所有等待线程会同时被唤醒,并且可以在唤醒前执行预定义的动作。如果在等待过程中有线程被中断,其他等待线程会抛出BrokenBarrierException
。 -
Semaphore
中获取许可的线程如果许可不足会被阻塞,直到有许可可用(其他线程释放许可)或者等待线程被中断。当有许可可用时,等待线程会被唤醒获取许可并继续执行访问共享资源的操作。
CountDownLatch
、CyclicBarrier
和 Semaphore
在 Java 并发编程中都有各自独特的作用和适用场景,合理选择和使用它们可以帮助开发人员更有效地协调多个线程之间的执行和资源共享,提高并发程序的性能和可靠性。
原文地址:https://blog.csdn.net/qq_62097431/article/details/143609232
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!