JUC 中常用的并发工具类

90 阅读2分钟

1 等待多线程完成的 CountDownLatch

CountDownLatch 允许一个或多个线程等待其他线程完成操作

private static CountDownLatch countDownLatch = new CountDownLatch(2);

private static void countDownLatch() throws InterruptedException {
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + ":start");
        countDownLatch.countDown();
    }, "t1").start();
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + ":start");
        countDownLatch.countDown();
    }, "t2").start();
    countDownLatch.await();
    System.out.println(Thread.currentThread().getName() + ":end");
}

2 同步屏障 CyclicBarrier

让一组线程达到一个屏障(同步点)时被阻塞

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);

private static void cyclicBarrier() throws Exception {
    new Thread(() -> {
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":start");
    }, "t1").start();
    new Thread(() -> {
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":start");
    }, "t2").start();
    // 执行到这里之后,各个线程才会开始执行
    cyclicBarrier.await();
    System.out.println(Thread.currentThread().getName() + ":end");
}

还有更高级的构造函数 public CyclicBarrier(int parties, Runnable barrierAction),用来当线程达到屏障时,优先执行 barrierAction

public class BankWaterService implements Runnable {

    private final static int COUNT = 4;
    private int result = 0;
    private CyclicBarrier c = new CyclicBarrier(COUNT, this);
    private static ExecutorService executor = Executors.newFixedThreadPool(COUNT);
    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

    private int getResult() {
        return this.result;
    }

    private void count() {
        for (int i = 0; i < COUNT; i++) {
            int finalI = i;
            executor.execute(() -> {
                // 计算流水数据
                map.put(Thread.currentThread().getName(), finalI + 1);
                // 插入一个屏障
                try {
                    c.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "end");
            });
        }
        System.out.println("count end");
    }

    @Override
    public void run() {
        for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
            result += stringIntegerEntry.getValue();
        }
        System.out.println("方法结束");
    }

    public static void main(String[] args) {
        BankWaterService bankWaterService = new BankWaterService();
        bankWaterService.count();
        SleepUtils.second(10);
        System.out.println(bankWaterService.getResult());
        executor.shutdown();
        System.out.println("主线程结束");
    }

}

CountDownLatch 和 CyclicBarrier 的区别

  • CyclicBarrier 可以使用 reset() 方法重置计数器
  • CyclicBarrier 提供 getNumberWaiting() 方法获得当前阻塞的线程数量
  • CyclicBarrier 提供 isBroken() 方法来了解阻塞的线程是否被中断了

3 控制并发线程数的 Semaphore

用来控制同时访问特定资源的线程数量

可以用作流量控制

private static final int THREAD_COUNT = 30;
private static ExecutorService pool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore semaphore = new Semaphore(10);

private static void semaphore() throws Exception {

    for (int i = 0; i < THREAD_COUNT; i++) {
        pool.execute(() -> {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + ":start");
                SleepUtils.second(2);
                semaphore.release();
                System.out.println(Thread.currentThread().getName() + ":end");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    pool.shutdown();
    System.out.println(Thread.currentThread().getName() + ":end");
}
  • 常用方法
    • accquire()
      • 阻塞获取许可证
    • tryAccquire()
      • 非阻塞获取许可证,没有就返回 false
    • release()
      • 释放许可证资源
    • availablePermits()
      • 返回当前可用许可证数
    • getQueueLength()
      • 获取目前正在等待获取许可证的线程数
    • reducePermits(int reduction)
      • 减少reduction个许可证

4 线程间交换数据的 Exchanger

是一个用于线程间协作的工具类,用于线程间数据交换

会提供一个同步点,两个线程在这个同步点进行数据交换

private static final Exchanger<String> exchanger = new Exchanger<>();
private static ExecutorService pool2 = Executors.newFixedThreadPool(2);


private static void exchanger() throws Exception {
    pool2.execute(() -> {
        try {
            String a = "流水A";
            String b = exchanger.exchange(a);
            System.out.println("A:" + a + "\tB:" + b + "\tA和B数据是否一致:" + b.equals(a));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    pool2.execute(() -> {
        try {
            String b = "流水B";
            SleepUtils.second(10);
            String a = exchanger.exchange(b);
            System.out.println("A:" + a + "\tB:" + b + "\tA和B数据是否一致:" + b.equals(a));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    pool2.shutdown();
}