一、CountDownLatch
1.CountDownLatch原理:
CountDownLatch 是基于计数器的原理实现的, 它内部维护了一个整型的计数器。创建 CountDownLatch 对象时, 需要指定一个初始计数值, 该计数值表示需要等待的线程数量,使用其 await() 方法让其等待。每当一个线程完成了其任务, 可以调用 CountDownLatch 的 countDown() 方法, 计数器的值就会减一。当计数器的值变成 0 时, 等待的线程就会被唤醒, 继续执行它们的任务。
2.示例:
class CountDownLatchDemo{public static void mian() throw InterruptedException{//创建倒计时门闩对象CountDownLatch latch = new CountDownLatch(5);for(int i = 1 ; i <= 5 ; i++){new Thread(()->{try {//模拟耗时操作Thread.sleep(1000);}catch (Exception e){e.printStackTrace();}System.out.println( i + "号线程执行完毕。");//计数器减一latch.countDown();} , i + "");}//主线程等待其他五个线程执行完后才继续执行downLatch.await();//await() 的重载方法,当计数器计数为0或者等待时间到达 5 秒,主线程会被唤醒//downLatch.await( 5 , TimeUnit.SECONDS );System.out.println( "其他线程全部执行完毕,主线程继续运行。");}
}
二、CyclicBarrier
1.CyclicBarrier原理:
CyclicBarrier 可以让一组线程互相等待,直到所有线程都到达某个公共屏障点(Barrier point)。它是一个循环栅栏,因为线程可以在屏障点再次达到后重新进行等待。CyclicBarrier 的使用需要指定两个参数:参与的线程数和屏障触发时的处理程序。当指定数量的线程到达屏障点时,所有线程都会被唤醒,并且可以执行一些共享的操作。在 CyclicBarrier 中,线程在等待时会释放所占用的资源,因此它适用于在多线程环境下同步多个线程完成任务的情况。它也常用于实现并行计算和并发测试的场景。同时,CyclicBarrier 是可以重复使用的,只需要调用其 reset() 方法,便可将屏障恢复至初始状态。
2.示例:
class CyclicBarrierDemo{public static void main(String[] args) { // 创建一个CyclicBarrier,指定参与的线程数和屏障触发时的处理程序 CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() { @Override public void run() { System.out.println("所有线程都已到达屏障点,开始执行任务...");// 在这里执行需要所有线程共同完成的任务 } }); // 创建3个线程,分别执行不同的任务 Thread thread1 = new Thread(()->{try {//模拟耗时操作Thread.sleep(1000);//线程到达屏障点,等待其他线程System.out.println( "1号线程到达屏障点。");barrier.await();//await() 的重载方法,当所有线程到达屏障点或者等待时间到达 5 秒,线程会被唤醒//barrier.await( 5 , TimeUnit.SECONDS);}catch (InterruptedException e){e.printStackTrace();}}, "Thread-1"); Thread thread2 = new Thread(()->{try {Thread.sleep(1000);System.out.println( "2号线程到达屏障点。");barrier.await();}catch (InterruptedException e){e.printStackTrace();}}, "Thread-2"); Thread thread3 = new Thread(()->{try {Thread.sleep(1000);System.out.println( "3号线程到达屏障点。");barrier.await();}catch (InterruptedException e){e.printStackTrace();}}, "Thread-3");// 启动线程thread1.start();thread2.start();thread3.start();//将屏障回复至初始状态,之后便可以再次使用//barrier.reset()}
}
三、Semaphore
1.Semaphore原理:
Semaphore 用于控制或限制对资源的访问。它是一个计数信号量,可以用来保护一段代码或共享资源,确保同时只有一个线程可以访问它,就类似抢车位。Semaphore 的使用需要创建一个 Semaphore 对象,并使用它的 acquire() 和 release() 方法来控制资源的访问。当一个线程需要访问资源时,它会调用 acquire() 方法来获取一个许可。如果许可可用,线程将获取许可并继续执行。如果许可不可用,线程将被阻塞,直到有可用的许可。当线程完成对资源的访问后,它会调用 release() 方法来释放许可,以便其他线程可以获取许可并访问资源。
2.示例:
class SemaphoreDemo{public static void main(String[] args) {// 创建一个初始许可数为2的Semaphore,代表有两个车位Semaphore semaphore = new Semaphore(2);//创建4个线程,代表4辆汽车for(int i = 1; i <= 4;i++){Thread thread = new Thread(() -> {try {// 获取一个许可,代表抢到车位semaphore.acquire();System.out.println(i + "号汽车抢到车位");// 模拟对资源的访问,代表停车Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}finally{// 释放许可,代表离开车位semaphore.release();System.out.println(i + "号汽车离开车位");}}).start();}
}
}