同步工具:CountDownLatch、CyclicBarrier和Semaphore

1. CountDownLatch

1.1 功能及使用场景

一个同步工具,使得一个或多个线程等待一组线程执行完成后再执行。

使用场景:等待一些前置任务执行完成后,再执行特定的功能。比如,系统启动时,各种配置生效后,才能运行提供服务。

1.2 代码实例

public class CountDownLatchTest {

    public static void main(String[] args) {
        final CountDownLatch latch = new CountDownLatch(5);

        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " end, " + System.currentTimeMillis());
                    latch.countDown();
                }
            }).start();
        }

        try {
            System.out.println("main latch.await() " + System.currentTimeMillis());
            latch.await();
            System.out.println("main latch.await() end " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

特别注意:CountDownLatch初始化时,数量一定要等于等待的线程的数量。

2. CyclicBarrier

2.1 功能及使用场景

CyclicBarrier(可循环同步屏障),控制一组线程相互等待,直到所有线程都到达屏障点。所有线程都到达屏障点后,同时开始执行剩余的任务。

可循环,意味着当所有线程都到达屏障后,屏障可以被再次重复利用。

使用场景

多个任务同时到达某个临界情况时,才能同时执行剩余任务,否则相互等待。

2.2 示例代码

public class CyclicBarrierTest {

    public static void main(String[] args) {
        int num = 3;
        final CyclicBarrier barrier = new CyclicBarrier(num);
        for (int i = 0; i < num; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName() + "执行结束,开始等待其它线程, " + System.currentTimeMillis());
                        barrier.await();
                        System.out.println(Thread.currentThread().getName() + "所有线程等待都执行完成,开始执行剩下任务, " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        // 屏障可以在上一次使用完成后被再次使用
        for (int i = 0; i < num; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName() + "执行结束,开始等待其它线程, " + System.currentTimeMillis());
                        barrier.await();
                        System.out.println(Thread.currentThread().getName() + "所有线程等待都执行完成,开始执行剩下任务, " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        System.out.println("main end at " + System.currentTimeMillis());
    }

}

3. Semaphore

3.1 功能及使用场景

Semaphore(信号量),一个计数信号量。概念上,一个信号量维持了一个许可集合。

  • acquire()方法:获取一个许可,如果没有获取到许可,则一直阻塞到获得一个许可;
  • release()方法:归还一个许可。

实际上,并没有真正的许可对象,只是计数。

使用场景

限制资源同时被访问的线程数量

3.2 使用代码

public class SemaphoreTest {

    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName() + "尝试获取许可, " + System.currentTimeMillis());
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "获取许可, " + System.currentTimeMillis());
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName() + "执行结束, " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }
                }
            }).start();
        }
        System.out.println("main end! " + System.currentTimeMillis());
    }

}

特别注意:获得许可并执行完逻辑后,一定要释放,否则许可不会被归还。(有借有还,再借不难)

4. 总结

  • CountDownLatch:一个任务等待前置任务执行完后才能执行
  • CyclicBarrier: 一组任务相互等待,直到所有线程均到达某个临界状态
  • Semaphore: 一组许可,控制资源被同时访问的数量(获取许可,也要归还许可)
posted @ 2018-04-13 11:43  Acode  阅读(233)  评论(0编辑  收藏  举报
您是本站第访问量位访问者!