JUC高并发编程7:辅助类

时间:2024-10-09 08:05:07

1 减少计数CountDownLatch

1.1 CountDownLatch 概述

CountDownLatch 是 Java 并发包(java.util.concurrent)中的一个同步工具类,用于在多个线程之间进行协调。它允许一个或多个线程等待其他线程完成一组操作。

1.1.1 主要方法

  • CountDownLatch(int count):构造方法,初始化一个计数器,计数器的初始值为 count
  • void countDown():将计数器的值减 1。
  • void await():使当前线程等待,直到计数器的值变为 0。
  • boolean await(long timeout, TimeUnit unit):使当前线程等待,直到计数器的值变为 0,或者等待超时。

1.1.2 工作原理

  1. 初始化计数器:通过构造方法 CountDownLatch(int count) 初始化一个计数器,计数器的初始值为 count
  2. 线程等待:一个或多个线程调用 await() 方法,这些线程会阻塞,直到计数器的值变为 0。
  3. 计数器减 1:其他线程调用 countDown() 方法,将计数器的值减 1。调用 countDown() 方法的线程不会阻塞。
  4. 唤醒等待线程:当计数器的值变为 0 时,因 await() 方法阻塞的线程会被唤醒,继续执行。

1.2 示例代码

场景: 6 个同学陆续离开教室后值班同学才可以关门

public class CountDownLatchDemo {
    // 6个同学陆续离开教室之后,班长锁门
    public static void main(String[] args) throws InterruptedException {
        // 创建CountDownLatch对象,设置初始值
        int count = 6;
        CountDownLatch countDownLatch = new CountDownLatch(count);

        // 6个同学陆续离开教室之后
        for (int i = 1; i <= count; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + " 号同学离开了教室");

                // 计数器 -1
                countDownLatch.countDown();

            },String.valueOf(i)).start();
        }

        // 
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName() + "班长锁门了");
    }
}

1.2.1 代码解释

  1. 初始化计数器
 int count = 6;
 CountDownLatch latch = new CountDownLatch(count);
  1. 创建并启动三个线程
for (int i = 1; i <= count; i++) {
	new Thread(()->{
    System.out.println(Thread.currentThread().getName() + " 号同学离开了教室");

    // 计数器 -1
    countDownLatch.countDown();

	},String.valueOf(i)).start();
}

  1. 主线程等待
// 主线程等待,直到计数器变为 0
 countDownLatch.await();

 System.out.println(Thread.currentThread().getName() + "班长锁门了");

1.3 小结

CountDownLatch 是一个非常有用的同步工具类,用于协调多个线程之间的执行顺序。通过初始化一个计数器,并使用 countDown()await() 方法,可以实现线程之间的等待和唤醒机制。在实际应用中,CountDownLatch 常用于等待一组线程完成某个任务后再继续执行后续操作。

2 循环栅栏CyclicBarrier

2.1 CyclicBarrier 概述

CyclicBarrier 是 Java 并发包(java.util.concurrent)中的一个同步工具类,用于协调多个线程之间的执行顺序。它允许一组线程互相等待,直到所有线程都到达一个共同的屏障点(barrier point),然后继续执行。

2.1.1 主要方法

  • CyclicBarrier(int parties):构造方法,初始化一个 CyclicBarrier,指定需要等待的线程数量。
  • CyclicBarrier(int parties, Runnable barrierAction):构造方法,初始化一个 CyclicBarrier,指定需要等待的线程数量,并在所有线程到达屏障点时执行一个指定的 Runnable 任务。
  • int await():使当前线程等待,直到所有线程都调用了 await() 方法,或者某个线程被中断。
  • int await(long timeout, TimeUnit unit):使当前线程等待,直到所有线程都调用了 await() 方法,或者某个线程被中断,或者等待超时。
  • int getParties():返回需要等待的线程数量。
  • boolean isBroken():查询此屏障是否处于损坏状态。
  • void reset():将屏障重置为其初始状态。

2.1.2 工作原理

  1. 初始化屏障:通过构造方法 CyclicBarrier(int parties)CyclicBarrier(int parties, Runnable barrierAction) 初始化一个 CyclicBarrier,指定需要等待的线程数量。
  2. 线程等待:每个线程调用 await() 方法,等待其他线程到达屏障点。
  3. 屏障点触发:当所有线程都调用了 await() 方法,屏障点被触发,所有线程继续执行。
  4. 可选的屏障动作:如果构造方法中指定了 Runnable 任务,则在所有线程到达屏障点后,执行该任务。
  5. 重用屏障CyclicBarrier 可以重复使用,当所有线程到达屏障点后,屏障会自动重置,等待下一组线程到达。

2.2 示例代码

场景: 集齐 7 颗龙珠就可以召唤神龙

public class CyclicBarrierDemo {

    // 创建固定值
    private static final int NUMBER = 7;

    public static void main(String[] args) {
        // 创建
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
            System.out.println("集齐七颗龙珠就可以召唤神龙");
        });

        // 集齐七颗龙珠过程
        for (int i = 1; i <= 7; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + " 星龙珠被收集了");

                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }

            },String.valueOf(i)).start();
        }
    }
}

2.2.1 代码解释

  1. 初始化屏障
  private static final int NUMBER = 7;

  CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
          System.out.println("集齐七颗龙珠就可以召唤神龙");
  });
  1. 创建并启动七个线程
for (int i = 1; i <= 7; i++) {
           new Thread(()->{
               System.out.println(Thread.currentThread().getName() + " 星龙珠被收集了");

               try {
               		// 等待其他线程
                   cyclicBarrier.await();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               } catch (BrokenBarrierException e) {
                   throw new RuntimeException(e);
               }

           },String.valueOf(i)).start();
       }

2.3 小结

CyclicBarrier 是一个非常有用的同步工具类,用于协调多个线程之间的执行顺序。通过初始化一个屏障,并使用 await() 方法,可以实现线程之间的等待和唤醒机制。CyclicBarrier 可以重复使用,适用于需要多个线程协同工作的场景。

3 信号灯Semaphore

3.1 Semaphore 概述

Semaphore 是 Java 并发包(java.util.concurrent)中的一个同步工具类,用于控制同时访问某个资源的线程数量。它通过许可证(permit)的概念来实现,每个许可证代表一个线程可以访问资源的权限。

3.1.1 主要方法

  • Semaphore(int permits):构造方法,初始化一个 Semaphore,指定最大信号量(许可证数量)。
  • Semaphore(int permits, boolean fair):构造方法,初始化一个 Semaphore,指定最大信号量(许可证数量),并设置是否使用公平锁(fairness)。
  • void acquire():获取一个许可证,如果没有可用的许可证,则阻塞当前线程,直到有许可证可用。
  • void acquire(int permits):获取指定数量的许可证,如果没有足够的许可证,则阻塞当前线程,直到有足够的许可证可用。
  • void release():释放一个许可证,将其返回给信号量。
  • void release(int permits):释放指定数量的许可证,将其返回给信号量。
  • int availablePermits():返回当前可用的许可证数量。
  • boolean tryAcquire():尝试获取一个许可证,如果成功则返回 true,否则返回 false
  • boolean tryAcquire(int permits):尝试获取指定数量的许可证,如果成功则返回 true,否则返回 false
  • boolean tryAcquire(long timeout, TimeUnit unit):尝试在指定时间内获取一个许可证,如果成功则返回 true,否则返回 false
  • boolean tryAcquire(int permits, long timeout, TimeUnit unit):尝试在指定时间内获取指定数量的许可证,如果成功则返回 true,否则返回 false

3.1.2 工作原理

  1. 初始化信号量:通过构造方法 Semaphore(int permits)Semaphore(int permits, boolean fair) 初始化一个 Semaphore,指定最大信号量(许可证数量)。
  2. 获取许可证:线程调用 acquire() 方法获取一个许可证,如果没有可用的许可证,则阻塞当前线程,直到有许可证可用。
  3. 释放许可证:线程调用 release() 方法释放一个许可证,将其返回给信号量。
  4. 控制并发访问:通过控制许可证的数量,可以限制同时访问某个资源的线程数量。

3.2 示例代码

场景: 抢车位, 6 部汽车 3 个停车位

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 创建Semaphore,设置许可数量
         int permits = 3;
        Semaphore semaphore = new Semaphore(permits);

        // 模拟6辆汽车
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{

                try {
                    //抢占
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到了车位");
                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                    System.out.println(Thread.currentThread().getName() + " ----离开了车位");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    // 释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}
代码解释
  1. 初始化信号量
 int permits = 3;
 Semaphore semaphore = new Semaphore(permits);
  1. 创建并启动五个线程
for (int i = 1; i <= 6; i++) {
     new Thread(()->{

         try {
             //抢占
             semaphore.acquire();
             System.out.println(Thread.currentThread().getName()+"抢到了车位");
             //设置随机停车时间
             TimeUnit.SECONDS.sleep(new Random().nextInt(5));

             System.out.println(Thread.currentThread().getName() + " ----离开了车位");
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
         } finally {
             // 释放
             semaphore.release();
         }
     },String.valueOf(i)).start();
 }

3.3 小结

Semaphore 是一个非常有用的同步工具类,用于控制同时访问某个资源的线程数量。通过初始化一个信号量,并使用 acquire()release() 方法,可以实现对并发访问的控制。Semaphore 适用于需要限制并发访问数量的场景,如资源池、连接池等。

4 思维导图

在这里插入图片描述

5 参考链接

【尚硅谷】大厂必备技术之JUC并发编程