Java并发编程——Semaphore (信号量)和CountDownLatch

一、Semaphore (信号量)

        说简单点,Semaphore维护了一个许可集合,在创建Semaphore的时候,设置上许可数,每条线程在只有在获得一个许可的时候才可以继续往下执行逻辑(申请一个许可,则Semaphore的许可池中减少一个许可),没有获得许可的线程会进入阻塞状态。

举个栗子:

public static void main(String[] args) {
        //创建一个Semaphore 有5条许可
        final Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //申请一个许可  许可池-1   若许可池为0则申请许可失败,阻塞线程
                        semaphore.acquire();
                        System.out.print(finalI);
                        //模仿耗时操作
                        Thread.sleep(1000);
                        //释放一个许可  许可池+1
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

然后看log的打印:

12-19 11:06:52.720 13842-13978/lbx.myapplication E/lbxlog: 0
12-19 11:06:52.720 13842-13981/lbx.myapplication E/lbxlog: 3
12-19 11:06:52.720 13842-13979/lbx.myapplication E/lbxlog: 1
12-19 11:06:52.720 13842-13980/lbx.myapplication E/lbxlog: 2
12-19 11:06:52.720 13842-13982/lbx.myapplication E/lbxlog: 4
//这里注意一下  仔细对比时间
12-19 11:06:53.720 13842-13983/lbx.myapplication E/lbxlog: 5
12-19 11:06:53.720 13842-13985/lbx.myapplication E/lbxlog: 7
12-19 11:06:53.720 13842-13987/lbx.myapplication E/lbxlog: 9
12-19 11:06:53.720 13842-13986/lbx.myapplication E/lbxlog: 8
12-19 11:06:53.720 13842-13984/lbx.myapplication E/lbxlog: 6

       看了上面的log,发现前五个是52秒左右打印出来的,而后五个是53秒左右打印出来的,因为许可池里只有5个许可,前五条线程把5个许可占用了,后五条线程需要等到有许可后才会继续执行逻辑。

接下来介绍几个常用的方法:

//释放1个许可
semaphore.release();
//使acquire()后正在等待的线程不可被终止
semaphore.acquireUninterruptibly();
//获取当前可用的许可数量,并把可用的许可数置0
semaphore.drainPermits();
//是否有正在等待的线程
semaphore.hasQueuedThreads();
//获取正在等待的线程数量
semaphore.getQueueLength();
//获取当前可用的许可数量
semaphore.availablePermits();

二、信号量的另一种形式:非公平信号量

        非公平信号量,先运行的线程不一定可以申请到许可,后运行的线程不一定不可以申请到许可。

//创建5个公平信号量
Semaphore semaphore = new Semaphore(5,true);
//尝试获得许可,并返回获取结果   if(acquireSuc)...else...
boolean acquireSuc = semaphore.tryAcquire();
boolean acquireSuc = semaphore.tryAcquire(2);
//在3秒内尝试获得1个许可,并返回获取结果
boolean acquireSuc = semaphore.tryAcquire(3, TimeUnit.SECONDS);
//在3秒内尝试获得2个许可,并返回获取结果
boolean acquireSuc = semaphore.tryAcquire(2, 3, TimeUnit.SECONDS);

三、CountDownLatch的应用

    这个东西和信号量正相反,CountDownLatch的内部维护着一个计数器,同时只有一个线程才可对计数器进行操作,当计数器为0的时候,释放所有阻塞的线程。

举个栗子:

public static void main(String[] args) {
        //4条线程
        int threadCount = 4;
        //创建一个拥有5个计数器的countDownLatch
        final CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < threadCount; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //使计数器-1
                    countDownLatch.countDown();
                    System.out.println("计数器还剩" + countDownLatch.getCount());
                }
            }).start();
        }

        try {
            //在这里阻塞  只有计数器为0的时候这行代码才会释放
             countDownLatch.await();
            } catch (InterruptedException e) {
             e.printStackTrace();
            }

  System.out.println("执行完成啦!"); 
}

log:
 

12-19 11:52:25.350 22929-23074/lbx.myapplication I/System.out: 计数器还剩4
12-19 11:52:25.350 22929-23073/lbx.myapplication I/System.out: 计数器还剩3
12-19 11:52:25.350 22929-23075/lbx.myapplication I/System.out: 计数器还剩2
12-19 11:52:25.350 22929-23076/lbx.myapplication I/System.out: 计数器还剩1

因为计数器没有到0,所以“执行完成”这句话没有打印,将线程改成5条:

//5线程
        int threadCount = 5;

log:

12-19 11:58:46.570 30347-30471/lbx.myapplication I/System.out: 计数器还剩4
12-19 11:58:46.570 30347-30473/lbx.myapplication I/System.out: 计数器还剩3
12-19 11:58:46.570 30347-30472/lbx.myapplication I/System.out: 计数器还剩2
12-19 11:58:46.570 30347-30474/lbx.myapplication I/System.out: 计数器还剩1
12-19 11:58:46.570 30347-30475/lbx.myapplication I/System.out: 计数器还剩0
12-19 11:58:46.570 30347-30347/lbx.myapplication I/System.out: 执行完成啦!

这时候完整的流程就执行完了。常用方法:

//使线程进入阻塞状态,计数为0的时候释放线程
countDownLatch.await();
//使线程进入阻塞状态,计数为0的时候释放线程或者阻塞到3秒的时候自动释放线程
countDownLatch.await(3, TimeUnit.SECONDS);
//使计数减1
countDownLatch.countDown();
//获取当前的计数
countDownLatch.getCount();

 

你可能感兴趣的:(Java)