1,概述
java并发包已经讲解了volatile,synchronized,CAS,AQS机制等。这些都是整个并发的基础,这篇主要讲一下并发包下的一些组件。
2,CountDownLatch
2.1 demo:
首先演示下这个组件的功能。主线程执行到await()方法后,就会同步等待2个线程执行,这里的2就是构造函数传入的。然后再执行下去。
public class CountDownLatchDemo { public static void main(String[] args) throws Exception{ final CountDownLatch latch = new CountDownLatch(2); new Thread() { @Override public void run() { try { Thread.sleep(1000); System.out.println("线程1执行..."); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } } }.start(); new Thread() { @Override public void run() { try { Thread.sleep(1000); System.out.println("线程2执行..."); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } } }.start(); System.out.println("主线程即将执行await..."); latch.await(); System.out.println("主线程执行..."); } }
2.2 构造函数:
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } //将传入的参数作为state的大小。这里的state即AQS下的state Sync(int count) { setState(count); }
2.3 await():
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
这里需要看看tryAcquireShared()方法:
protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
很明显,state是我们传入的2,不等于0,会进入方法doAcquireSharedInterruptibly():
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
这个方法前面都分析过,直接画图了:
然后其他线程会执行countDown()方法,源码逻辑很简单,就是依次去减state的值。然后判断state是否等于0,如果state==0,则唤醒队列中main线程。可以发现,熟悉AQS原理之后,这些都是很简单的,所以AQS是并发的基础。
3,CyclicBarrier
3.1 demo:
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() { @Override public void run() { System.out.println("召唤神龙。。。。。。。。。。。"); } }); for (int i = 1; i <= 3; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println("收集第" + Thread.currentThread().getName() + "颗"); try { cyclicBarrier.await(); //等待被唤醒,唤醒后的操作 } catch (Exception e) { e.printStackTrace(); } } }, String.valueOf(i)).start(); } } }
3.2 构造函数:
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); }
构造函数有两种,一种是带Runable,一种是不带的。
3.3 await()方法
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } }
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; 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(); } } // 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(); } }
通过源码可以知道,await()方法就是充分利用ReentractLock和Condition来实现的。
具体流程说一下:
1,parties=3,线程1进来,将parties减1,变成2.获取锁成功,然后将自己入condition队列,释放锁,挂起
2,这时候可能线程2和线程3已经进入等待队列等着获取锁了。假设这时候线程2获取锁,将parties减1,变成1,然后将自己入condition队列,释放锁,挂起
3,线程3获取锁成功,将parties减1,变i成0.满足条件,会执行传入的Runable对象的run方法。然后将之前的在condition队列的线程执行signalAll(),也就是将其转换到等待队列,然后释放锁。
4,这时候等待队列的线程陆续获取锁,并释放,整个流程跑通。