JUC工具类CountDownLatch、CyclicBarrier、Semaphore介绍

发布时间 2023-10-05 16:19:55作者: 斌哥的小弟

CountDownLatch:它是一种同步工具,用于控制一个或多个线程等待其他线程完成操作后再继续执行。它的作用是让某个线程等待一组操作执行完成,再继续执行自己的任务。CountDownLatch 内部有一个计数器,当计数器的值为0时,等待的线程将会被唤醒。通过 CountDownLatch 的构造函数可以指定计数器的初始值,并通过 countDown() 方法递减计数器的值,通过 await() 方法使线程等待计数器达到0。

 

案例(模拟一个教室,里面有五个学生一个班长,当五个学生都离开教室后班长才能锁门):

 public static void main(String[] args) {
        int numThreads = 5;
        CountDownLatch latch = new CountDownLatch(numThreads);

        // 创建并启动多个线程
        for (int i = 0; i < numThreads; i++) {
            Thread thread = new Student(latch); //通过这样的方式将latch锁放到线程里
            thread.start();
        }

        try {
            // 主线程等待所有工作线程完成
            latch.await();
            System.out.println("班长锁门");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class Student extends Thread {
        private CountDownLatch latch;

        public Student(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"同学离开教室");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
        }
    }

 

 

CyclicBarrier(循环栅栏):它也是一种同步工具,用于控制多个线程互相等待,直到所有线程都达到一个共同的屏障点,然后再一起继续执行后续操作。与 CountDownLatch 不同的是,CyclicBarrier 的计数器是可循环使用的,当计数器达到指定值时,所有等待的线程被释放,计数器会被重置。CyclicBarrier 的构造函数可以指定等待的线程数,并提供一个指定的动作(Runnable),该动作会在所有线程达到屏障点后执行。

其构造方法和使用案例类似于减少计数。

 

Semaphore(信号量):它是一种用来控制同时访问某个资源的线程数量的同步辅助类。Semaphore 内部维护了一组许可证(permit),线程在访问资源前需要先获取许可证,如果许可证数量为0,则需要等待其他线程释放许可证。当线程使用完资源后,需要释放许可证,以供其他等待线程使用。Semaphore 可以用来限制同时访问某个资源的线程数量,也可以作为一种计数器来统计通过某个点的线程数量。

 

案例:

private static final int MAX_CONCURRENT_THREADS = 5;
    private static Semaphore semaphore = new Semaphore(MAX_CONCURRENT_THREADS);

    public static void main(String[] args) {
        // 创建并启动多个访问线程
        for (int i = 0; i < 10; i++) {
            Thread thread = new WorkerThread(i);
            thread.start();
        }
    }

    static class WorkerThread extends Thread {
        private int threadIndex;

        public WorkerThread(int index) {
            this.threadIndex = index;
        }

        @Override
        public void run() {
            try {
                System.out.println("Thread " + threadIndex + " 正在等待许可证...");
              
      semaphore.acquire();

                System.out.println("Thread " + threadIndex + " 获取到许可证,开始访问资源");
                // 模拟线程访问资源的耗时操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
                System.out.println("Thread " + threadIndex + " 释放了许可证");
            }
        }
    }

常用方法:

获取许可证:线程可以通过调用 acquire() 方法来请求一个许可证。如果有可用的许可证,则从计数器中减去一个许可证,并继续执行。如果没有可用的许可证,线程将被阻塞,直到有许可证可用或者线程被中断。

释放许可证:线程在使用完资源后,需要调用 release() 方法来释放许可证。该操作将会将计数器中的许可证数量加一,如果有其他线程在等待许可证,其中一个线程将会被唤醒并获取到许可证。