深入浅出剖析Semaphore, CyclicBarrier, CountDownLatch并发工具并用ReentrantLock实现
2024-01-03 11:42:58
前言
在多线程编程中,经常需要协调多个线程之间的动作,以确保程序的正确执行。Java并发工具类提供了许多有用的工具来帮助我们管理和协调线程之间的协作,Semaphore、CyclicBarrier和CountDownLatch就是其中最为常见的三个。
Semaphore
Semaphore是一个信号量,它允许一定数量的线程同时访问某个资源。Semaphore通常用于限制对共享资源的访问,防止资源被过度使用。
Semaphore的构造函数接受一个参数,即许可证的数量。许可证的数量决定了同时可以访问共享资源的线程数。例如,如果Semaphore的许可证数量为5,则最多有5个线程可以同时访问共享资源。
Semaphore提供了两个主要方法:acquire()和release()。acquire()方法用于获取一个许可证,如果当前没有可用的许可证,则该线程将被阻塞,直到其他线程释放一个许可证。release()方法用于释放一个许可证,这将允许另一个线程获取许可证并访问共享资源。
CyclicBarrier
CyclicBarrier是一个循环屏障,它允许一组线程在等待所有线程都到达某个点之后继续执行。CyclicBarrier通常用于同步一组线程,以确保它们在继续执行之前都完成各自的任务。
CyclicBarrier的构造函数接受两个参数:线程数和屏障动作。线程数指定了等待屏障的线程数,屏障动作是一个Runnable对象,它将在所有线程到达屏障后执行。
CyclicBarrier提供了两个主要方法:await()和reset()。await()方法用于等待所有线程都到达屏障,如果当前不是所有线程都到达屏障,则该线程将被阻塞,直到其他线程到达屏障。reset()方法用于重置屏障,这将允许另一组线程等待屏障。
CountDownLatch
CountDownLatch是一个倒计时闩锁,它允许一组线程等待某个事件发生。CountDownLatch通常用于同步一组线程,以确保它们在事件发生之前都处于等待状态。
CountDownLatch的构造函数接受一个参数,即倒计时的初始值。倒计时的初始值指定了等待事件发生的线程数。
CountDownLatch提供了两个主要方法:await()和countDown()。await()方法用于等待事件发生,如果当前事件尚未发生,则该线程将被阻塞,直到事件发生。countDown()方法用于递减倒计时,当倒计时减至0时,事件发生,所有等待的线程将被唤醒。
使用ReentrantLock实现Semaphore、CyclicBarrier和CountDownLatch
我们可以使用ReentrantLock来实现Semaphore、CyclicBarrier和CountDownLatch这三个并发工具。
使用ReentrantLock实现Semaphore
我们可以使用ReentrantLock和Condition来实现Semaphore。ReentrantLock用于控制对共享资源的访问,Condition用于等待许可证的可用。
public class SemaphoreImpl implements Semaphore {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private int permits;
public SemaphoreImpl(int permits) {
this.permits = permits;
}
@Override
public void acquire() throws InterruptedException {
lock.lock();
try {
while (permits <= 0) {
condition.await();
}
permits--;
} finally {
lock.unlock();
}
}
@Override
public void release() {
lock.lock();
try {
permits++;
condition.signal();
} finally {
lock.unlock();
}
}
}
使用ReentrantLock实现CyclicBarrier
我们可以使用ReentrantLock和Condition来实现CyclicBarrier。ReentrantLock用于控制对屏障的访问,Condition用于等待所有线程都到达屏障。
public class CyclicBarrierImpl implements CyclicBarrier {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private final int parties;
private final Runnable barrierAction;
private int count;
public CyclicBarrierImpl(int parties, Runnable barrierAction) {
this.parties = parties;
this.barrierAction = barrierAction;
}
@Override
public int await() throws InterruptedException, BrokenBarrierException {
lock.lock();
try {
count++;
if (count == parties) {
barrierAction.run();
count = 0;
condition.signalAll();
} else {
while (count != parties) {
condition.await();
}
}
return parties - 1;
} finally {
lock.unlock();
}
}
}
使用ReentrantLock实现CountDownLatch
我们可以使用ReentrantLock和Condition来实现CountDownLatch。ReentrantLock用于控制对闩锁的访问,Condition用于等待闩锁的打开。
public class CountDownLatchImpl implements CountDownLatch {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private int count;
public CountDownLatchImpl(int count) {
this.count = count;
}
@Override
public void await() throws InterruptedException {
lock.lock();
try {
while (count > 0) {
condition.await();
}
} finally {
lock.unlock();
}
}
@Override
public void countDown() {
lock.lock();
try {
count--;
if (count == 0) {
condition.signalAll();
}
} finally {
lock.unlock();
}
}
}
总结
Semaphore、CyclicBarrier和CountDownLatch是Java并发工具类中非常有用的三个工具,它们可以帮助我们管理和协调线程之间的协作。在本文中,我们剖析了这三个工具的内部实现原理,并使用ReentrantLock实现了它们。