CyclicBarrier的使用

释放双眼,带上耳机,听听看~!

CyclicBarrier和CountDownLatch一样在使用时也需要在构造方法中传递一个int类型的参数,但这个参数代表的含义和在使用上与CountDownLatch有所区别,这个参数代表的是拦截线程的数量,当线程调用CyclicBarrier中的await()方法时,就是告诉CyclicBarrier我已经拦截了当前线程,也就是此时调用await()方法的线程将被阻塞,线程中await()方法后面的代码将不会执行,当前线程会一直等待,一到调用await()方法的线程数量与CyclicBarrier构造方法中参数的数量一致时,线程才会重新执行。下面我们看一下简单的例子来演示一下CyclicBarrier的使用。

public class Test {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(String.format("开始: %s", Thread.currentThread().getName()));
                    for (int i = 10; i > 0; i--) {
                        System.out.println(String.format("计时:%s	thread: %s", i, Thread.currentThread().getName()));
                        Thread.sleep(1000);
                    }
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务: %s", Thread.currentThread().getName()));
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(String.format("开始: %s", Thread.currentThread().getName()));
                    for (int i = 5; i > 0; i--) {
                        System.out.println(String.format("计时:%s	thread: %s", i, Thread.currentThread().getName()));
                        Thread.sleep(1000);
                    }
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务: %s", Thread.currentThread().getName()));
            }
        });
        thread1.start();
        thread2.start();
        System.out.println(String.format("任务: %s", Thread.currentThread().getName()));
    }
}
开始: Thread-1
任务: main
开始: Thread-0
计时:5	thread: Thread-1
计时:10	thread: Thread-0
计时:9	thread: Thread-0
计时:4	thread: Thread-1
计时:8	thread: Thread-0
计时:3	thread: Thread-1
计时:7	thread: Thread-0
计时:2	thread: Thread-1
计时:6	thread: Thread-0
计时:1	thread: Thread-1
计时:5	thread: Thread-0
计时:4	thread: Thread-0
计时:3	thread: Thread-0
计时:2	thread: Thread-0
计时:1	thread: Thread-0
任务: Thread-1
任务: Thread-0

下面我们详细分析一下输出的信息。我们在CyclicBarrier构造方法中传递的参数是2也就是说它要拦截2个线程的await()方法后才会执行await()方法后面的逻辑。所以在调用await()方法之前线程是不会阻塞的。所以会出输出开始、计时等信息,我们会发现当线程Thread-1已经执行完循环任务时,并没有马上输出await()方法后面的代码,而是此线程阻塞了,原因是Thread-0还没有实现完,它还没有执行await()方法,也就是说拦截的线程数量还没有达到CyclicBarrier构造方法中传递数量,所以线程Thread-1就会一直阻塞直到Thread-0执行完循环并执行await()方法时,此时拦截的线程数量已经等于构造方法中的参数了,所以线程Thread-1才会恢复执行。这就是CyclicBarrier的使用。

CyclicBarrier与CountDownLatch的区别:

  • CountDownLatch阻塞的是主线程
  • CountDownLatch的计数器只能使用一次也就是只能递减
  • CyclicBarrier阻塞的是子线程
  • CyclicBarrier的计数器可以使用reset()方法重置

CyclicBarrier还提供了另一个构造方法除了传递int类型的参数外,还可以传递一个Runnable类型。它的目的是,当线程的拦截的数量与构造方法的参数相等时,优先执行构造方法里的任务,然后在执行每个线程中await()方法后面的代码。

public class Test {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
            @Override
            public void run() {
                System.out.println(String.format("拦截: %s", Thread.currentThread().getName()));
            }
        });
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(String.format("开始: %s", Thread.currentThread().getName()));
                    for (int i = 10; i > 0; i--) {
                        System.out.println(String.format("计时:%s	thread: %s", i, Thread.currentThread().getName()));
                        Thread.sleep(1000);
                    }
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务: %s", Thread.currentThread().getName()));
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(String.format("开始: %s", Thread.currentThread().getName()));
                    for (int i = 5; i > 0; i--) {
                        System.out.println(String.format("计时:%s	thread: %s", i, Thread.currentThread().getName()));
                        Thread.sleep(1000);
                    }
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任务: %s", Thread.currentThread().getName()));
            }
        });
        thread1.start();
        thread2.start();
        System.out.println(String.format("任务: %s", Thread.currentThread().getName()));
    }
}
开始: Thread-0
开始: Thread-1
计时:10	thread: Thread-0
计时:5	thread: Thread-1
任务: main
计时:4	thread: Thread-1
计时:9	thread: Thread-0
计时:8	thread: Thread-0
计时:3	thread: Thread-1
计时:7	thread: Thread-0
计时:2	thread: Thread-1
计时:6	thread: Thread-0
计时:1	thread: Thread-1
计时:5	thread: Thread-0
计时:4	thread: Thread-0
计时:3	thread: Thread-0
计时:2	thread: Thread-0
计时:1	thread: Thread-0
拦截: Thread-0
任务: Thread-0
任务: Thread-1
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧