目录
什么是CountDownLatch
CountDownLatch
允许一个或多个线程等待一组操作在其他线程中完成。CountDownLatch
是一次性的,计数器的值只能设置一次,且一旦倒数到零,就不能被重置。
使用CountDownLatch
方法
1. countDown()
-
作用:递减
CountDownLatch
中的计数器。 -
使用场景:通常在线程完成其工作后调用此方法。每次调用
countDown()
方法,计数器的值就会减少一。 -
特点:
-
如果计数器的值已经为零,则调用
countDown()
方法不会有任何效果。 -
计数器的值只能递减,不能递增。
-
一旦计数器的值递减到零,就不能被重置。
-
2. await()
-
作用:使当前线程等待,直到
CountDownLatch
中的计数器递减到零。 -
使用场景:通常在需要等待其他线程完成工作时使用。例如,主线程需要等待所有子线程执行完毕后再继续执行。
-
特点:
-
如果计数器的值已经为零,则调用
await()
方法会立即返回,不会阻塞当前线程。 -
如果计数器的值大于零,则调用
await()
方法的线程会被阻塞,直到计数器的值递减到零。 -
await()
方法可以被中断。如果当前线程在等待时被中断,则会抛出InterruptedException
异常。
-
案例
public static void main(String[] args) throws InterruptedException {
// 计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " Start");
countDownLatch.countDown(); // 计数器-1
}, String.valueOf(i)).start();
}
// 阻塞等待计数器归零
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " End");
}
本案例中,countDown()
方法用于递减计数器,而 await()
方法用于等待计数器递减到零。通过这种方式,主线程能够确保所有子线程都执行完毕后再继续执行。
什么是CyclicBarrier
CyclicBarrier
允许一组线程相互等待,直到所有线程都到达一个公共屏障点(Barrier)。与 CountDownLatch
不同,CyclicBarrier
是可重用的,可以在计数器归零后重置,因此可以用于多个场景下的循环同步。
使用CyclicBarrier
方法
1. await()
-
作用:使当前线程在屏障点等待,直到所有线程都到达屏障点。
-
异常:
-
InterruptedException
:如果当前线程在等待时被中断。在这种情况下,线程会退出等待状态,并抛出InterruptedException
。 -
BrokenBarrierException
:如果屏障被破坏(例如,其他线程在等待时被中断或超时)。在这种情况下,所有正在等待的线程都会抛出BrokenBarrierException
。
-
案例
public class CyclicBarrierDemo {
public static void main(String[] args) {
// CyclicBarrier(int parties, Runnable barrierAction)
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("7个线程执行完毕");
});
for (int i = 1; i <= 7; i++) {
final int tempInt = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "这是第" + tempInt + "个线程");
try {
cyclicBarrier.await(); // 等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
本案例中,我们创建了一个 CyclicBarrier
对象,指定了参与者数量为 7,并设置了一个屏障动作,当所有线程都到达屏障点时,会打印 "7个线程执行完毕"。然后创建了 7 个线程,每个线程在执行完任务后都会调用 cyclicBarrier.await()
方法到达屏障点等待。当所有线程都到达屏障点时,屏障动作会被执行。
什么是Semaphore
Semaphore
用于控制同时访问某个特定资源的线程数量。Semaphore
通过维护一组许可证来实现对资源的访问控制,每个许可证代表对资源的一次访问权限。
使用Semaphore
方法
1. acquire()
-
作用:从
Semaphore
中获取一个许可证。如果没有可用的许可证,则阻塞当前线程,直到获取到一个许可证。 -
特点:
-
如果当前有可用的许可证,则立即获取一个,并减少可用许可证的数量。
-
如果没有可用的许可证,则当前线程会被阻塞,直到其他线程释放许可证。
-
-
异常:如果当前线程在等待许可证时被中断,则抛出
InterruptedException
。
2. release()
-
作用:释放一个许可证,将其返回给
Semaphore
。这会增加可用的许可证数量,可能会唤醒正在等待许可证的线程。 -
特点:
-
释放许可证后,
Semaphore
中的可用许可证数量会增加。 -
如果有其他线程正在等待许可证,则可能会唤醒一个或多个等待的线程。
-
案例
public static void main(String[] args) {
// 创建一个Semaphore,初始许可证数量为3
Semaphore semaphore = new Semaphore(3);
// 创建5个线程,模拟对资源的访问
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
// 获取一个许可证
semaphore.acquire();
System.out.println("线程" + Thread.currentThread().getName() + "访问资源");
// 模拟资源访问时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放一个许可证
semaphore.release();
}
}, "Thread-" + i).start();
}
}
本案例中,Semaphore
被用于限制对某个资源的并发访问。首先创建了一个 Semaphore
对象,初始许可证数量为 3。然后创建了 5 个线程,每个线程在执行时都会尝试获取一个许可证。如果获取到许可证,则访问资源,并在访问完成后释放许可证。