CountDownLatch(倒计时器)
是什么:
CountDownLatch是Java中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作。
使用场景:
- 当一个线程需要等待多个其他线程执行完毕后才能继续执行时,可以使用CountDownLatch。例如,启动一个服务时,主线程可能需要等待多个组件加载完毕。
- 实现多个线程开始执行任务的最大并行性。即多个线程在同一时刻开始执行,类似于赛跑中的发令枪响后所有选手同时起跑。
实现原理:
CountDownLatch内部维护了一个计数器,通过countDown()方法减少计数器的值,通过await()方法让线程在计数器归零之前处于等待状态。当计数器的值变为0时,所有在CountDownLatch上await()的线程将被唤醒。
CountDownLatch示例
import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { int numberOfThreads = 3; CountDownLatch countDownLatch = new CountDownLatch(numberOfThreads); for (int i = 0; i < numberOfThreads; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " 准备完毕!"); countDownLatch.countDown(); // 计数器减1 }).start(); } countDownLatch.await(); // 等待计数器归零 System.out.println("所有线程准备完毕,可以开始比赛了!"); }
}
示例说明:在CountDownLatchExample
中,我们创建了一个倒计时门闩,初始化为3,然后启动了3个线程。每个线程在完成准备工作后调用countDown()
方法,主线程通过await()
方法等待所有线程准备完毕。
CyclicBarrier(循环屏障)
是什么:
CyclicBarrier也是Java中的一个同步辅助类,它允许一组线程互相等待,直到所有线程都到达某个公共屏障点(common barrier point)后才能继续执行。
使用场景:
CyclicBarrier常用于需要多个线程相互协作,共同完成某个任务的场景。例如,一个寝室的四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,等四个人都到齐后一起出发去球场。
实现原理:
CyclicBarrier内部维护了一个计数器和一个屏障点。每当一个线程调用await()方法时,计数器的值就会减1。当计数器的值减至0时,表示所有线程都已到达屏障点,此时屏障会被打开,所有等待的线程将继续执行。CyclicBarrier还可以设置一个可选的Runnable对象,在所有线程到达屏障点时执行该对象的run()方法。
CycliBarrier示例:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; public class CyclicBarrierExample { public static void main(String[] args) { int numberOfThreads = 4; CyclicBarrier cyclicBarrier = new CyclicBarrier(numberOfThreads); for (int i = 0; i < numberOfThreads; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " 已到达集合点"); try { cyclicBarrier.await(); // 等待所有线程到达集合点 } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 出发去球场!"); }).start(); } }
}
示例说明: 在CyclicBarrierExample
中,我们创建了一个循环屏障,需要4个线程到达后才能继续。每个线程在到达集合点后调用await()
方法等待其他线程,当所有线程都到达后,它们将一起继续执行。
Semaphore(信号量)
是什么:
Semaphore是Java中的一个用于控制同时访问特定资源的线程数量的同步工具。
使用场景:
Semaphore主要用于那些资源有明确访问数量限制的场景,常用于限流。例如,数据库连接池、线程池或同时访问某个接口的请求数量等。通过Semaphore可以控制同时访问资源的线程数量,防止因资源过度使用而导致的系统崩溃或性能下降。
实现原理:
Semaphore内部维护了一个计数器和多个等待队列。计数器的值表示当前可用的资源数量(即信号量数量)。当线程调用acquire()方法时,会尝试获取一个信号量(即将计数器减1),如果当前没有可用的信号量,则线程会被阻塞并放入等待队列中。当其他线程释放信号量(即调用release()方法将计数器加1)时,会从等待队列中唤醒一个或多个线程来获取信号量并继续执行。这种方式可以保证对资源的有序访问和合理利用。
Semaphore示例:
import java.util.concurrent.Semaphore; public class SemaphoreExample { public static void main(String[] args) { int maxPermits = 2; // 最大允许同时访问的资源数量 Semaphore semaphore = new Semaphore(maxPermits); 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(); // 释放一个许可 System.out.println(Thread.currentThread().getName() + " 离开了停车场"); } }).start(); } }
}
示例说明:在SemaphoreExample
中,我们创建了一个信号量,允许最多2个线程同时访问资源(这里是模拟的停车场)。每个线程在访问资源前需要调用acquire()
方法获取许可,访问完成后调用release()
方法释放许可。这样可以确保同时访问资源的线程数量不会超过信号量设定的最大值。