Java中的并发工具类:CountDownLatch、CyclicBarrier、Semaphore详解
在Java中,CountDownLatch
、CyclicBarrier
和 Semaphore
是常用的并发工具类,用于协调多线程之间的同步。它们分别适用于不同的场景,下面将详细解释它们的用法和区别。
1. CountDownLatch
CountDownLatch
是一个同步辅助工具,允许一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器来实现,计数器的初始值由构造函数指定。每当一个线程完成了自己的任务,计数器就会减1。当计数器减到0时,等待的线程就会被唤醒。
主要方法:
-
CountDownLatch(int count)
:构造函数,初始化计数器。 -
void await()
:使当前线程等待,直到计数器减到0。 -
void countDown()
:将计数器减1。
使用场景:
-
主线程等待多个子线程完成任务后再继续执行。
-
多个线程等待某个事件发生后再同时开始执行。
示例代码:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int numThreads = 3;
CountDownLatch latch = new CountDownLatch(numThreads);
for (int i = 0; i < numThreads; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " is running");
latch.countDown();
}).start();
}
latch.await();
System.out.println("All threads have finished");
}
}
2. CyclicBarrier
CyclicBarrier
是一个同步辅助工具,允许一组线程互相等待,直到所有线程都到达某个屏障点(barrier point)后再继续执行。与 CountDownLatch
不同,CyclicBarrier
可以重复使用,即当所有线程都到达屏障点后,计数器会重置,可以再次使用。
主要方法:
-
CyclicBarrier(int parties)
:构造函数,初始化屏障的线程数。 -
CyclicBarrier(int parties, Runnable barrierAction)
:构造函数,初始化屏障的线程数,并指定当所有线程到达屏障点时执行的屏障操作。 -
int await()
:使当前线程等待,直到所有线程都到达屏障点。
使用场景:
-
多个线程需要相互等待,直到所有线程都到达某个点后再继续执行。
-
可以用于分阶段的任务,每个阶段都需要所有线程完成后再进入下一个阶段。
示例代码:
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int numThreads = 3;
CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
System.out.println("All threads have reached the barrier");
});
for (int i = 0; i < numThreads; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " is waiting at the barrier");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " has passed the barrier");
}).start();
}
}
}
3. Semaphore
Semaphore
是一个计数信号量,用于控制同时访问某个资源的线程数量。它通过维护一组许可(permits)来实现,线程在访问资源之前必须先获取许可,访问结束后释放许可。如果许可被全部占用,其他线程必须等待,直到有许可被释放。
主要方法:
-
Semaphore(int permits)
:构造函数,初始化许可数量。 -
void acquire()
:获取一个许可,如果没有许可可用,则线程阻塞。 -
void release()
:释放一个许可。 -
boolean tryAcquire()
:尝试获取一个许可,如果成功返回true,否则返回false。
使用场景:
-
控制对有限资源的访问,例如数据库连接池、线程池等。
-
限制同时执行的线程数量。
示例代码:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
int numPermits = 2;
Semaphore semaphore = new Semaphore(numPermits);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " has acquired a permit");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + " has released a permit");
}
}).start();
}
}
}
总结
-
CountDownLatch:适用于一个或多个线程等待其他线程完成操作后再继续执行的场景。
-
CyclicBarrier:适用于多个线程相互等待,直到所有线程都到达某个屏障点后再继续执行的场景,且可以重复使用。
-
Semaphore:适用于控制同时访问某个资源的线程数量,常用于资源池的管理。
根据具体的需求选择合适的并发工具类,可以有效地协调多线程之间的同步,提高程序的并发性能。