Semaphore用法

Semaphore的用法能够实现CountDownLatch和CyclicBarrier的功能,与这两者不同的是,Semaphore的计数器是累加的,当它的值等于acquire(n);要求的n时线程才能被唤醒。

Semaphore实现CountDownLatch的功能

public class SemaphoreTest {
    // 初始化信号量计数器为0
    private static Semaphore semaphore=new Semaphore(0);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 将线程A添加到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A:"+Thread.currentThread()+"开始执行任务");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程A:"+Thread.currentThread()+"执行任务结束");
                // 调用release信号量计数器加1
                semaphore.release();
            }
        });

        // 将线程B添加到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B:"+Thread.currentThread()+"开始执行任务");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程B:"+Thread.currentThread()+"执行任务结束");
                // 调用release信号量计数器加1
                semaphore.release();
            }
        });
        // 等到信号量计数器=2,取消阻塞
        semaphore.acquire(2);
        System.out.println("所有线程都执行完毕");
        executorService.shutdown();
    }
}

执行结果:

线程A:Thread[pool-1-thread-1,5,main]开始执行任务
线程B:Thread[pool-1-thread-2,5,main]开始执行任务
线程B:Thread[pool-1-thread-2,5,main]执行任务结束
线程A:Thread[pool-1-thread-1,5,main]执行任务结束
所有线程都执行完毕

结果分析:
开始Semaphore初始化计数器为0,主线程创建完两个线程后,调用semaphore.acquire(2);会阻塞。当线程A执行完调用semaphore.release();后,计数器的值会加1,同理线程B执行完后,计数器的值等于2。这时候已经满足semaphore.acquire(2);的要求,主线程继续往下执行。

Semaphore实现CyclicBarrier的功能

public class SemaphoreTest2 {

    private static Semaphore semaphore=new Semaphore(0);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 将线程A添加到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A:"+Thread.currentThread()+"开始执行任务第1步");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程A:"+Thread.currentThread()+"执行任务第1步结束");
                semaphore.release();
            }
        });

        // 将线程B添加到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B:"+Thread.currentThread()+"开始执行任务第1步");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程B:"+Thread.currentThread()+"执行任务第1步结束");
                semaphore.release();
            }
        });

        semaphore.acquire(2);

        // 将线程A1添加到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A:"+Thread.currentThread()+"开始执行任务第2步");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程A:"+Thread.currentThread()+"执行任务第2步结束");
                semaphore.release();
            }
        });

        // 将线程B1添加到线程池
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B:"+Thread.currentThread()+"开始执行任务第2步");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程B:"+Thread.currentThread()+"执行任务第2步结束");
                semaphore.release();
            }
        });

        semaphore.acquire(2);
        System.out.println("所有线程都执行完毕");
        executorService.shutdown();
    }
}

执行结果:

线程A:Thread[pool-1-thread-1,5,main]开始执行任务第1步
线程B:Thread[pool-1-thread-2,5,main]开始执行任务第1步
线程A:Thread[pool-1-thread-1,5,main]执行任务第1步结束
线程B:Thread[pool-1-thread-2,5,main]执行任务第1步结束
线程A:Thread[pool-1-thread-1,5,main]开始执行任务第2步
线程B:Thread[pool-1-thread-2,5,main]开始执行任务第2步
线程A:Thread[pool-1-thread-1,5,main]执行任务第2步结束
线程B:Thread[pool-1-thread-2,5,main]执行任务第2步结束
所有线程都执行完毕

结果分析:
这里模拟了CyclicBarrier计数器复用的特性,主线程创建完线程A和线程B后阻塞,线程A和线程B执行完任务分别调用release使得主线程被唤醒,主线程继续创建线程A1和线程B1又阻塞,线程A1和线程B1执行完任务分别调用release使得主线程被唤醒,主线程继续执行后续逻辑。

CountDownLatch、CyclicBarrier、Semaphore三者总结

CountDownLatch
CountDownLatch countDownLatch=new CountDownLatch(n);
通过调用countDownLatch.await();阻塞,每调用countDownLatch.countDown();1次,计数器n会减1,当n=0时,所有阻塞的线程被唤醒。

CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(n);
通过调用cyclicBarrier.await();阻塞,同时计数器n会减1,当n=0时,所有阻塞的线程被唤醒。n能够实现复用,也就是n被重置为原来的值,唤醒后的线程可以继续await阻塞,然后等待n=0,继续被唤醒。

Semaphore
Semaphore semaphore=new Semaphore(n);
与前两者对比,这里的计数器是递增的。通过调用semaphore.acquire(m);阻塞,如果m=n则被唤醒。线程通过调用semaphore.release();来使得计数器n加1,一旦使得n=m,那么所有阻塞的线程被唤醒。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值