AQS应用-CyclicBarrier

生活

生活就是今天不想吃午饭,然后去楼下买了个面包,发现面包很难吃还是强忍着吃了几口。剩下的留到明早做早餐,

场景

写代码写累了,想去看看大海!
于是我报了个去南麂岛的旅游团,听说那里很美。
然后。。。
今天的故事场景就来了。
既然是跟团游,势必要跟着导游走。早上6点30(不得不吐槽,每次跟团旅游都很早,累得要死哈哈)到达指定上车点。上车等了许久都不出发,事实上大部分人都已经到了,就等1、2个起不来床的。一直到7点才正式出发。

这个场景简单的说就是人满发车,用程序的世界来描述就是,一个系统内多个线程在某一个屏障处互相等待直到所有线程到达。

这里要引入的就是CyclicBarrier,回环屏障或曰回环栅栏。
看名字就知道这个玩意是可以循环使用的。

简述

CyclicBarrier 也是并发包下非常重要的。
下面先来了解一下如何创建一个回环栅栏。

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

查看源码可以看到CyclicBarrier有两个构造器,最终都调用到CyclicBarrier(int parties, Runnable barrierAction) 的构造器,
parties 代表成员数
barrierAction 不为空 则在所有线程都到达屏障后 先执行barrierAction,在唤醒其他等待线程往下执行。

下面是该类的public 方法 了解一下:

//等待
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen;
        }
    }
 //  设置超时时间,超时后不管没到的线程,就是不等没来的人就开了
public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }
//是否已经被破坏
public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }

// 获取成员总数
    public int getParties() {
        return parties;
    }

//获取当前在屏障处等待的线程数
public int getNumberWaiting() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return parties - count;
        } finally {
            lock.unlock();
        }
    }
//重置,这就是为什么称之为回环
public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

以上的看看就行,代码也很简单,回环栅栏的关键在于dowait方法,下面来看看doWait到底做了啥?
直接上源码:

 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();
            }

//计数-1
           int index = --count;
           //如果是最后一个到达的
           if (index == 0) {  // tripped
               boolean ranAction = false;
               try {
               //如果有传入预先要执行的线程先执行
                   final Runnable command = barrierCommand;
                   if (command != null)
                       command.run();
                   ranAction = true;
                   //然后唤醒其他线程
                   nextGeneration(); //这里有一个trip.signalAll();唤醒其他线程
                   return 0;
               } finally {
                   if (!ranAction)
                       breakBarrier();
               }
           }

            // loop until tripped, broken, interrupted, or timed out
            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 {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        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();
        }
    }

这个类的实现用到了ReentrantLock和Condition,关于这两个东西具体后面再说,这里知道condition.await()阻塞线程让他进入等待队列,condition.signall()唤醒所有线程即可。

案例

下面来看下案例。

案例1:无限期等待,不设置超时。

public class CBTest {
	
	public static void main(String[] args) {
		CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Guide());
		Long start = System.currentTimeMillis();
		new Thread(new Visitor("jack", cyclicBarrier, start)).start();
		new Thread(new Visitor("bob", cyclicBarrier, start)).start();
		new Thread(new Visitor("lucy", cyclicBarrier, start)).start();
		new Thread(new Visitor("lily", cyclicBarrier, start)).start();
		new Thread(new Visitor("john", cyclicBarrier, start)).start();

	}
	
	static class Guide implements Runnable{

		@Override
		public void run() {
			System.out.println("------------------人到齐了---------------------");
			System.out.println("导游分发帽子、矿泉水");
			System.out.println("-----------------------------------------");
			
		}
		
	}
	
	static class Visitor implements Runnable{
		private String name;
		private CyclicBarrier cyclicBarrier;
		private Long start;
		
		
		
		public Visitor(String name, CyclicBarrier cyclicBarrier, Long start) {
			super();
			this.name = name;
			this.cyclicBarrier = cyclicBarrier;
			this.start = start;
		}



		@Override
		public void run() {
			try {
				Thread.sleep(Long.valueOf(new Random().nextInt(2000)));
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			System.out.println(String.format("name:%s,已经到了!用时:%s ms",name,System.currentTimeMillis()-start));
			try {
				cyclicBarrier.await();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			System.out.println(String.format("name:%s,出发了",name));
			
		}
		
		
		
	}

}

执行结果:

name:jack,已经到了!用时:565 ms
name:bob,已经到了!用时:1172 ms
name:lily,已经到了!用时:1231 ms
name:john,已经到了!用时:1333 ms
name:lucy,已经到了!用时:1478 ms
------------------人到齐了---------------------
导游分发帽子、矿泉水
-----------------------------------------
name:lily,出发了
name:john,出发了
name:bob,出发了
name:jack,出发了
name:lucy,出发了

案例2:设置等待超时,超时的就不管了,谁叫他们墨迹呢


public class CBTest {
	
	public static void main(String[] args) {
		CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Guide());
		Long start = System.currentTimeMillis();
		new Thread(new Visitor("jack", cyclicBarrier, start)).start();
		new Thread(new Visitor("bob", cyclicBarrier, start)).start();
		new Thread(new Visitor("lucy", cyclicBarrier, start)).start();
		new Thread(new Visitor("lily", cyclicBarrier, start)).start();
		new Thread(new Visitor("john", cyclicBarrier, start)).start();

	}
	
	static class Guide implements Runnable{

		@Override
		public void run() {
			System.out.println("------------------人到齐了---------------------");
			System.out.println("导游分发帽子、矿泉水");
			System.out.println("-----------------------------------------");
			
		}
		
	}
	
	static class Visitor implements Runnable{
		private String name;
		private CyclicBarrier cyclicBarrier;
		private Long start;
		
		
		
		public Visitor(String name, CyclicBarrier cyclicBarrier, Long start) {
			super();
			this.name = name;
			this.cyclicBarrier = cyclicBarrier;
			this.start = start;
		}



		@Override
		public void run() {
			try {
				Thread.sleep(Long.valueOf(new Random().nextInt(5000)));
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			System.out.println(String.format("name:%s,已经到了!用时:%s ms",name,System.currentTimeMillis()-start));
			try {
			//等待超时时间两秒
				cyclicBarrier.await(2, TimeUnit.SECONDS);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (TimeoutException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(String.format("name:%s,出发了",name));
			
		}
		
		
		
	}

}

执行结果:

name:lucy,已经到了!用时:906 ms
name:bob,已经到了!用时:1282 ms
name:jack,已经到了!用时:2516 ms
java.util.concurrent.BrokenBarrierException
name:jack,出发了
name:lucy,出发了
	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
	at test.CBTest$Visitor.run(CBTest.java:60)
	at java.lang.Thread.run(Unknown Source)
java.util.concurrent.TimeoutException
	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
	at test.CBTest$Visitor.run(CBTest.java:60)
	at java.lang.Thread.run(Unknown Source)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
	at test.CBTest$Visitor.run(CBTest.java:60)
	at java.lang.Thread.run(Unknown Source)
name:bob,出发了
name:lily,已经到了!用时:3808 msjava.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)

	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
	at test.CBTest$Visitor.run(CBTest.java:60)
	at java.lang.Thread.run(Unknown Source)
name:lily,出发了
name:john,已经到了!用时:3832 ms
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
	at java.util.concurrent.CyclicBarrier.await(Unknown Source)
	at test.CBTest$Visitor.run(CBTest.java:60)
	at java.lang.Thread.run(Unknown Source)
name:john,出发了

超时后抛出异常唤醒其他线程,继续往下走。超时的线程到达屏障后同样抛出异常往下走,此时导游不在分发帽子和矿泉水了,这个线程不会被执行了!

思考

CountDownLatch与CyclicBarrier的区别?
1、
表面上:
前者只能使用一次,因为没有重置state的方法。
后者可以通过reset方法重置后循环使用。
场景上:
前者是一个或多个线程等待其他线程执行计数减的操作后,往下执行。
后者是多个线程之间在一个屏障处互相等待直到所有线程都到了才往下执行。

通过CountDownLatch实现CyclicBarrier


public class CBTest2 {
	
	public static void main(String[] args) {
		CountDownLatch countDownLatch = new CountDownLatch(1);
		Long start = System.currentTimeMillis();
		new Thread(new Visitor("jack", countDownLatch, start)).start();
		new Thread(new Visitor("bob", countDownLatch, start)).start();
		new Thread(new Visitor("lucy", countDownLatch, start)).start();
		new Thread(new Visitor("lily", countDownLatch, start)).start();
		new Thread(new Visitor("john", countDownLatch, start)).start();
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		countDownLatch.countDown();

	}
	
	static class Guide implements Runnable{

		@Override
		public void run() {
			System.out.println("------------------人到齐了---------------------");
			System.out.println("导游分发帽子、矿泉水");
			System.out.println("-----------------------------------------");
			
		}
		
	}
	
	static class Visitor implements Runnable{
		private String name;
		private CountDownLatch countDownLatch;
		private Long start;
		
		
		
		public Visitor(String name,CountDownLatch countDownLatch, Long start) {
			super();
			this.name = name;
			this.countDownLatch = countDownLatch;
			this.start = start;
		}



		@Override
		public void run() {
			try {
				Thread.sleep(Long.valueOf(new Random().nextInt(1000)));
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			System.out.println(String.format("name:%s,已经到了!用时:%s ms",name,System.currentTimeMillis()-start));
			try {
				countDownLatch.await();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(String.format("name:%s,出发了",name));
			
		}
		
		
		
	}

}

在一定场景下,可以使用CountDownLatch来实现CyclicBarrier,不过,,谁那么蛋疼,有CyclicBarrier,干嘛要用CountDownLatch来实现呢~~

后记

加油,真好!

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84296613