Java多线程与并发编程 | CountDownLatch、Semaphore

430 阅读2分钟

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

前言

JUC包中给我们提供了很多工具类,CountDownLatchSemaphore等。

CountDownLatch

字面翻译为倒计时闩锁,结合字面意思,想象一下一个门闩上面记着一个数字,倒计时到0后打开门闩,放行。我们可以利用这个特性,实现类似计数器的功能。比如有一个任务A,他要等待其他3个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能。

代码示例

public class CountDownSample {
    private static int count = 0;
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(100);
        //CDL总数和操作数保持一致
        CountDownLatch cdl = new CountDownLatch(10000); 
        for (int i = 1; i <= 10000; i++) {
            final int index = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    synchronized (CountDownSample.class) {
                        try {
                            count = count + index;
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            //计数器减一
                            cdl.countDown();
                        }
                    }
                }
            });
        }
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(count);
        threadPool.shutdown();
    }
}
  • cdl.await()堵塞当前线程,知道cdl=0的时候再继续往下走
  • 还记得之前验证多线程的时候为了保证循环里的线程都走完,在程序中加了一段延迟睡眠,在这里我们就可以通过CountDownLatch来控制了。

Semaphore

Semaphore信号量经常用于限制获取某种资源的线程数量。举个例子,游戏厂商在同一时间玩家在线数量过多的时候会限制登录人数,其余的人就得排队,限制在线人数10000,那么这里的信号量就是10000,当在线的人有人退出,排队的人就会立马顶替上去。 代码示例

public class SemaphoreSample1 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        Semaphore semaphore = new Semaphore(5);
        for(int i = 1 ; i <= 20 ; i++) {
            final int index = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        play();
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            });
        }
        threadPool.shutdown();
    }

    public static void play(){
        try {
            System.out.println(new Date() + " " + Thread.currentThread().getName() + ":获取登录名额,登录成功");
            Thread.sleep(2000);
            System.out.println(new Date() + " " + Thread.currentThread().getName() + ":退出服务器");
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • Semaphore semaphore = new Semaphore(5)定义5个信号量,也就是说服务器只允许5个人在里面玩
  • semaphore.acquire()获取一个信号量,“占用一个名额”
  • play()模拟玩的动作,2.5s之后退出
  • semaphore.release()执行完成后释放这个信号量,“退出登录,让出名额”

除了排队的场景,我们还碰到过,如果人数已满,直接拒绝登录,请稍后再试。在上面的代码基础上稍加修改,就可以实现这样的功能。 代码示例

public void run() {
    try {
        if(semaphore.tryAcquire(6, TimeUnit.SECONDS)) {
            play();
            semaphore.release();
        }else{
            System.out.println(Thread.currentThread().getName() + ":对不起,服务器已满,请稍后再试");
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}
  • semaphore.tryAcquire(6, TimeUnit.SECONDS)尝试获取一次信号量,6秒钟内获取到返回true,否则返回false 注意: 这种情况下,获取不到就直接拒绝访问。