CountDownLatch / Semaphore 示例及使用场景

AQS系列

1、AQS核心原理
2、ReentrantLock 示例及原理
3、CountDownLatch / Semaphore 示例及使用场景
4、BlockingQueue 示例及使用场景

一、基本原理

《AQS核心原理》 篇中提到的,AQS 内部有一个 state 标记状态,CountDownLatch 和 Semaphore 都是借助这个状态标记作为一个计数器来实现控制多个线程的,计数器的初始值为线程的数量,每个线程执行完后计数器会减一,当计数器的值是 0 的时候,表示所有的线程都已经完成了任务,我们可以看看构成方法如下:

public CountDownLatch(int count) {
    
    
 if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

CountDownLatch 主要 API 是 CountDownLatch.countDown(),CountDownLatch.await() 。

public Semaphore(int permits) {
    
    
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
    
    
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

Semaphore 主要 API 是 Semaphore.acquire() throws InterruptedException(阻塞并获取许可),Semaphore.release()(释放许可),Semaphore.tryAcquire() (尝试阻塞并获取许可)。

二、4人短跑赛 (CountDownLatch)

用一个运动会的短跑比赛作为场景,用 CountDownLatch 来实现,主要由四个运动员赛跑,等裁判一声枪响后,四个人都开始全力跑,不过比赛完成是需要四个人都跑完才算,不能是跑完一个人就算比赛结束(需要每个线程执行结束主线程才能执行)。

//实例化 CountDownLatch, 线程数量为 4
    static final CountDownLatch latch = new CountDownLatch(4);
    public static void main(String[] args) {
    
    
        System.out.println("开始准备赛跑,所有参赛者准备");
        new Thread(() -> {
    
    
            try {
    
    
                Thread.sleep(2000);
                latch.countDown();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("第一个人跑完了,一片欢呼声~~~~~~~~~~~");
        }).start();
        System.out.print("第一个人准备好了|");
        new Thread(() -> {
    
    
            try {
    
    
                Thread.sleep(4000);
                latch.countDown();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
                System.out.println("第二个人跑完了,好多人鼓掌~~~~~~~~~~~");
        }).start();
        System.out.print("第二个人准备好了|");
        new Thread(() -> {
    
    
            try {
    
    
                Thread.sleep(5000);
                latch.countDown();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
                System.out.println("第三个人跑完了,没几个人鼓掌~~~~~~~~~~~");
        }).start();
        System.out.print("第三个人准备好了|");
        new Thread(() -> {
    
    
            try {
    
    
                Thread.sleep(7000);
                latch.countDown();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("第四个人跑完了~~~~~~~~~~~");
        }).start();
        System.out.println("第四个人准备好了");
        System.out.println("发令枪~~~~pong...");
        System.out.println("裁判员在等待着...");
        try {
    
    
            latch.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("赛跑项目结束了。");
    }

执行结果如下:

开始准备赛跑,所有参赛者准备
第一个人开始跑了|第二个人开始跑了|第三个人开始跑了|第四个人开始跑了
发令枪~~~~pong...
裁判员在等待着...
第一个人跑完了,一片欢呼声~~~~~~~~~~~
第二个人跑完了,好多人鼓掌~~~~~~~~~~~
第三个人跑完了,没几个人鼓掌~~~~~~~~~~~
第四个人跑完了~~~~~~~~~~~
赛跑项目结束了。

从执行结果中可以看出,四个人全部跑完 赛跑项目才结束,也就是 4 个线程都执行完之后,才回到主线程执行。

三、请求限流(Semaphore)

用一个请求限流的场景,用 Semaphore 来模拟这个场景实现。
首先实例化一个Semaphore信号量 是 3,表示每次只能有3 个线程执行任务,用一个 内部类 RequestTask 来模拟请求,每次请求耗时 2 秒。

public static void main(String[] args) {
    
    
  //初始化信号量为 3,每次只有3个线程执行
    Semaphore semaphore = new Semaphore(3);
    for (int i = 0; i < 10; i++) {
    
    
        new Thread(new RequestTask(semaphore, "请求任务" + i)).start();
    }
}

static class RequestTask implements Runnable{
    
    
    Semaphore semaphore;
    String taskName;

    public RequestTask(Semaphore semaphore, String taskName) {
    
    
        this.semaphore = semaphore;
        this.taskName = taskName;
    }

    @Override
    public void run() {
    
    
        try {
    
    
            semaphore.acquire();
            System.out.println(taskName + "任务执行中...");
            TimeUnit.SECONDS.sleep(2);
            System.out.println(taskName + "任务执行完成...");
            semaphore.release();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

执行结果如下:

请求任务1任务执行中...
请求任务2任务执行中...
请求任务0任务执行中...
请求任务2任务执行完成...
请求任务0任务执行完成...
请求任务1任务执行完成...
请求任务4任务执行中...
请求任务5任务执行中...
请求任务3任务执行中...
请求任务3任务执行完成...
请求任务5任务执行完成...
请求任务4任务执行完成...
请求任务7任务执行中...
请求任务6任务执行中...
请求任务8任务执行中...
请求任务6任务执行完成...
请求任务7任务执行完成...
请求任务9任务执行中...
请求任务8任务执行完成...
请求任务9任务执行完成...

从执行结果可以看出,总共 10 个线程,每次只有 3 个线程执行,最后一个时 任务9 执行,也就是说,每次是最多有 3 个线程执行。

猜你喜欢

转载自blog.csdn.net/qq_19283249/article/details/128589797