并发编程-AQS锁:CountDownLatch、Semaphore、CyclicBarrier

1.CountDownLatch概念及使用

1.1概念

倒计时锁CountDownLatch
* 需要合理设计锁的数量,在每个线程中都需要调用countDown()方法
* 主线程调用await()方法,只有当 锁的数量变为 0 的时候,主线程才会继续向下推进

核心方法:

countDown:

  • 功能:将计数器的值减1。
  • 用途:通常在一个线程完成其工作后调用,以通知其他线程可以继续执行。
  • 计数器:当计数器减到零时,所有在 await() 方法上等待的线程将被唤醒。
  • 线程安全:此方法是线程安全的,多个线程可以同时调用。

await:

  • 功能:使当前线程等待,直到计数器的值为零。
  • 用途:当一个线程需要等待其他线程完成某些工作时,可以调用这个方法。
  • 阻塞:调用 await() 的线程会被阻塞,直到其他线程调用 countDown() 方法将计数器减到零。
  • 异常:如果线程在等待时被中断,则会抛出 InterruptedException

1.2Demo

public class CountDownLatchDemo01 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        long start = System.currentTimeMillis();
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("任务1执行了");
            countDownLatch.countDown();
        }).start();
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("任务2执行了");
            countDownLatch.countDown();
        }).start();
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("任务3执行了");
            countDownLatch.countDown();
        }).start();

        countDownLatch.await();

        long end = System.currentTimeMillis();
        System.out.println("主线程执行, time = " + (end - start));
    }
}

1.3执行结果

首先定义CountDownLatch的数量为3(内部AQS的state=3),同时启动三个线程,每个线程执行完成后,调用countDown方法。

主线程调用await()方法等待,当三个线程执行都调用countDown后,CountDownLatch为0,此时主线程继续执行。time约等于1s。

2.Semaphore概念及使用

2.1概念

Semaphore是一种信号量,它有一个初始的许可数量。线程可以获取一个许可来访问共享资源,释放资源时返回一个许可。(默认是非公平的)
* 特点:
* 许可数量:可以设置信号量的初始许可数量。
* 获取许可:线程可以获取一个许可来访问共享资源。
* 释放许可:线程完成访问后释放一个许可。

核心方法:

1.acquire()

  • 功能:获取一个许可(permit)。如果当前没有可用的许可,调用线程会被阻塞,直到有可用的许可。
  • 用途:当一个线程需要访问受限资源时,调用此方法来获取许可。
  • 阻塞:如果没有可用的许可,调用线程会阻塞,直到其他线程调用 release() 方法释放许可。
  • 异常:如果线程在等待许可时被中断,则会抛出 InterruptedException

2.release()

  • 功能:释放一个许可。增加计数器的值,允许其他等待的线程获取许可。
  • 用途:当线程完成对共享资源的访问后,调用此方法以释放许可。
  • 注意事项:调用 release() 方法的次数必须与 acquire() 方法的次数相匹配。否则,可能会导致 IllegalStateException
  • 线程安全release() 是线程安全的,可以被多个线程同时调用。

2.2Demo

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore lock = new Semaphore(3);// 定义三个信号量

        for (int i = 0; i < 10; i++){// 同时启动10个线程任务,每秒只能同时有三个任务执行
            new Thread(() -> {
                try {
                    lock.acquire();// 获取锁
                    System.out.println(Thread.currentThread().getName() + " 执行了任务");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                  lock.release();// 释放锁
                }
            }, "Thread - " + i).start();
        }
    }
}

2.3执行结果

在执行的过程中,每次都是三个线程同时执行;只有当有一个线程释放信号量后,才会唤醒一个等待线程。(测试代码中,每个线程模拟睡眠1s) 

3.CyclicBarrier概念及使用

3.1概念

CyclicBarrier 是 Java 并发包中的一个同步工具,用于使一组线程在某个共同点(即障碍)上相互等待,直到所有线程都到达这个点。它可以重复使用,适合在多次迭代中需要同步的场景。

主要概念

  • CyclicBarrier:允许一组线程互相等待,直到到达某个公共屏障。线程在到达屏障时会被阻塞,直到所有参与的线程都到达该屏障。
  • 重用性:与 CountDownLatch 不同,CyclicBarrier 可以在所有线程都通过屏障后重置并重新使用。
  • 计数器CyclicBarrier 维护一个计数器,表示需要到达的线程数量。

主要方法

  1. await():线程调用此方法,表示它到达了屏障。如果不是所有参与的线程都调用了 await(),那么调用此方法的线程会被阻塞,直到所有线程都到达屏障。
  2. getNumberWaiting():返回当前在等待到达屏障的线程数量。
  3. getParties():返回此屏障需要等待的线程数。

3.2Demo

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        int numberOfThreads = 5;
        CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, () -> {
            System.out.println("All parties have arrived at the barrier, let's proceed.");
        });

        for (int i = 0; i < 7; i++) {// 模拟7个线程 但是只有5个线程可以执行屏障点后的代码
            new Thread(new Task(barrier)).start();
        }
    }
}

class Task implements Runnable {
    private CyclicBarrier barrier;
    public Task(CyclicBarrier barrier) {
        this.barrier = barrier;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");
        try {
            barrier.await();
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

3.3执行结果

在这个例子中,模拟启动7个线程,当有5个线程执行await方法后,屏障被打开,5个线程可以继续执行;而另外2个线程只能等待。(如果还存在3个线程调用await,那么屏障会被再次打开,否则这两个线程就一直处于阻塞状态)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值