线程同步器CycliBarrier你都不会吗,打击到了我。。

线程同步器CycliBarrier你都不会吗,打击到了我。

上文介绍了CountDownLatch,CountDownLatch虽然可以实现多个线程同步,但是只能使用一次。而本文所要介绍的CycliBarrier,比CountDownLatch强大许多,可以使用多次。下面会从源码角度通透解析CyclicBarrier,觉得不错可以点赞收藏、觉得不好的话,欢迎评论区指正。

两个例子

CyclicBarrier实现一次线程同步,两个线程都执行完之后,才会进入下一步

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main{
    //初始值为2,指定同步两个线程
    public static CyclicBarrier barrier = new CyclicBarrier(2);
    public static void main(String args[]){
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new Runnable(){
            @Override
            public void run() {
                try{
                    System.out.println("Thread1 start");
                    //doSomething
                    barrier.await();
                    System.out.println("Thread1 end");
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        });
        pool.submit(new Runnable(){
            @Override
            public void run() {
                try{
                    System.out.println("Thread2 start");
                    //doSometing
                    barrier.await();
                    System.out.println("Thread2 end");
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        });
        pool.shutdown();
    }
}

CycliBarrier实现两阶段同步,线程一和线程二完成第一阶段后,才能进入第二阶段。可以看出CycliBarrier于CountDownLatch的区别,可以实现多次同步

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main{
    //初始值为2,指定同步两个线程
    public static CyclicBarrier barrier = new CyclicBarrier(2);
    public static void main(String args[]){
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new Runnable(){
            @Override
            public void run() {
                try{
                    System.out.println("Thread1 step1 start");
                    //doSomething
                    barrier.await();
                    System.out.println("Thread1 step1 end");

                    System.out.println("Thread1 step2 start");
                    //doSomething
                    barrier.await();
                    System.out.println("Thread1 step2 end");

                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        });
        pool.submit(new Runnable(){
            @Override
            public void run() {
                try{
                    System.out.println("Thread2 step1 start");
                    //doSomething
                    barrier.await();
                    System.out.println("Thread2 step1 end");

                    System.out.println("Thread2 step2 start");
                    //doSomething
                    barrier.await();
                    System.out.println("Thread2 step2 end");
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        });
        pool.shutdown();
    }
}
// Thread1 step1 start
// Thread2 step1 start
// Thread1 step1 end
// Thread2 step1 end
// Thread2 step2 start
// Thread1 step2 start
// Thread1 step2 end
// Thread2 step2 end

CycliBarrier类图

  • parties:同步线程数量
  • count:计数器,初始为parties,调用一次await()方法,减一
  • barrierCommand: count计数器为0时,会执行barrierCommand
  • lock: ReentrantLock类型,用于控制线程同步
  • trip: Condition类型,由lock.newConditon()产生
  • generation:

源码分析

前言:一次同步过程简称为一代

初始化

注意一点,参数parties,不仅赋值给变量parties,而且赋值给count

public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

await方法

调用dowait方法

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            //没有超时设置超时机制
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
}

dowait方法

  • 调用dowait方法会将count减一
  • 如果count减一后,不为零,则进入trip的阻塞队列
  • 减一后,为零,唤醒阻塞队列里的所有线程。更新状态,准备下一代。
  • 线程唤醒后,检查generation引用指向(进入下一代后,generation会重新指向),指向变化了,说明进入了下一代,当前代结束,所以可以直接返回。
private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;
		   //发生异常	
            if (g.broken)
                throw new BrokenBarrierException();
			
            //中断,导致退出同步过程
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;
            //如果count==0,代表冲破屏障,可以进入下一代(下一次同步过程)
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    //执行方法
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    //进入下一代
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }
            //减一后不为0,进入trip条件变量的阻塞队列
            for (;;) {
                try {
                    if (!timed)
                        //进入阻塞队列
                        trip.await();
                    else if (nanos > 0L)
                        //如果有超时机制,设置相应时间
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        Thread.currentThread().interrupt();
                    }
                }
                /***唤醒后的几种情况**/
			   //异常情况
                if (g.broken)
                    throw new BrokenBarrierException();
			   
                //进入了下一代情况
                if (g != generation)
                    return index;
			  
                //超时情况
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

nextGeneration方法

注意上一代结束后,count计数器更新为parties值,这是可以多次使用究极原因

private void nextGeneration() {
        // 唤醒上一代阻塞的线程
        trip.signalAll();
        // 更新状态,准备下一代
        count = parties;
        generation = new Generation();
}

参考文章

Java并发编程之美

发布了204 篇原创文章 · 获赞 678 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/zycxnanwang/article/details/105662631