JDK15源码(三):CountDownLatch

使用实例

class CountDownLatchThread extends Thread {
    
    

    private CountDownLatch countDownLatch;

    public CountDownLatchThread(CountDownLatch countDownLatch) {
    
    
        this.countDownLatch = countDownLatch;
    }

    public void run() {
    
    
        System.out.println(Thread.currentThread().getName());
        try {
    
    
            Thread.sleep(10*1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        countDownLatch.countDown();
    }

}

public class CountDownLatchDemo {
    
    

    public static void main(String[] args) {
    
    
        CountDownLatch countDownLatch = new CountDownLatch(2);
        
        CountDownLatchThread thread1 = new CountDownLatchThread(countDownLatch);
        thread1.start();

        CountDownLatchThread thread2 = new CountDownLatchThread(countDownLatch);
        thread2.start();
        try {
    
    
            countDownLatch.await();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("end");
    }
    
}

实例化

CountDownLatch(int count)构造函数里实例化了一个属性的值sync。Sync是CounDownLatch私有的不可变的静态内部类

private final Sync sync;

public CountDownLatch(int count) {
    
    
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

Sync继承了队列同步器AbstractQueuedSynchronizer(简称AQS),提供了一个修改AQS的state变量的构造函数。重写了AQS的tryAcquireShared和tryReleaseShared方法。

private static final class Sync extends AbstractQueuedSynchronizer {
    
    
    private static final long serialVersionUID = 4982264981922014374L;

    //修改AQS state变量的值
    Sync(int count) {
    
    
        setState(count);
    }

    //获取state变量的值
    int getCount() {
    
    
        return getState();
    }

    //判断state变量的值是否等于0,如果等于0,返回1;如果不等于0,返回-1。
    protected int tryAcquireShared(int acquires) {
    
    
        return (getState() == 0) ? 1 : -1;
    }

    //无限循环直到state的值为0或者cas修改state的值成功。
    protected boolean tryReleaseShared(int releases) {
    
    
        // Decrement count; signal when transition to zero
        for (;;) {
    
    
            //获取state的值
            int c = getState();
            //如果state的值为0,直接返回false。
            if (c == 0)
                return false;
            //state值减去1    
            int nextc = c - 1;
            //cas修改state的值
            if (compareAndSetState(c, nextc))
                //判断state的值是否等于0,如果等于0,返回true,如果不等于0,返回false。
                return nextc == 0;
        }
    }
}

await()方法

CountDownLatch的await方法调用了AQS的acquireSharedInterruptibly方法,入参是1。

public void await() throws InterruptedException {
    
    
    sync.acquireSharedInterruptibly(1);
}

Thread.interrupted()判断线程是否是中断状态,如果是,返回true。
tryAcquireShared(arg)判断state值是否等于0。等于0,返回1;不等于0,返回-1。
如果线程不是中断的,并且state的值不等于0,就会调用AQS的acquire方法。

public final void acquireSharedInterruptibly(int arg)

    throws InterruptedException {
    
    
    if (Thread.interrupted() ||
        (tryAcquireShared(arg) < 0 &&
         acquire(null, arg, true, true, false, 0L) < 0))
        throw new InterruptedException();
}

AQS#acquire方法将当前线程加入到队列中。
(1)第一次循环:first=false,node=null,pred=null,当first为true或者pred为null会去尝试获取锁,如果tryAcquireShared方法返回1,表示锁没有被占用。acquire方法直接返回1。如果tryAcquireShared方法返回-1,表示还有线程没有释放锁(也就是没有countDown),实例化node节点。
(2)第二次循环:first=false,node!=null,pred=node.prev=null,调用tryAcquireShared方法,如果state值不是0,初始化头结点,头尾结点指向相同结点。
(3)第三次循环:first=false,node!=null,pred=node.prev=null,如果调用tryAcquireShared方法返回不是1,cas将尾结点设置为node结点。如果设置成功,将前尾结点的next结点设置为node结点。node结点的prev结点设置为前尾结点。
(4)第四次循环:first=false,pred != null,head=pred ⇒ first=true,如果调用tryAcquireShared方法返回不是1,node结点的state属性初始值是0,修改为等待状态。
(5)第五次循环:first=true,如果调用tryAcquireShared方法返回不是1,调用LockSupport.park方法阻塞当前线程。

first=true表示是第一个排队的结点,如果调用tryAcquireShared方法返回是1,且是第一个排队的结点,将当前结点作为头结点,并唤醒下一个结点。

final int acquire(Node node, int arg, boolean shared,
                      boolean interruptible, boolean timed, long time) {
    
    
    Thread current = Thread.currentThread();
    byte spins = 0, postSpins = 0;   // retries upon unpark of first thread
    boolean interrupted = false, first = false;
    Node pred = null;                // predecessor of node when enqueued

    for (;;) {
    
    
        if (!first && (pred = (node == null) ? null : node.prev) != null &&
            !(first = (head == pred))) {
    
    
            if (pred.status < 0) {
    
    
                cleanQueue();           // predecessor cancelled
                continue;
            } else if (pred.prev == null) {
    
    
                Thread.onSpinWait();    // ensure serialization
                continue;
            }
        }
        if (first || pred == null) {
    
    
            boolean acquired;
            try {
    
    
                if (shared)
                    acquired = (tryAcquireShared(arg) >= 0);
                else
                    acquired = tryAcquire(arg);
            } catch (Throwable ex) {
    
    
                cancelAcquire(node, interrupted, false);
                throw ex;
            }
            if (acquired) {
    
    
                if (first) {
    
    
                    node.prev = null;
                    head = node;
                    pred.next = null;
                    node.waiter = null;
                    if (shared)
                        signalNextIfShared(node);
                    if (interrupted)
                        current.interrupt();
                }
                return 1;
            }
        }
        if (node == null) {
    
                     // allocate; retry before enqueue
            if (shared)
                node = new SharedNode();
            else
                node = new ExclusiveNode();
        } else if (pred == null) {
    
              // try to enqueue
            node.waiter = current;
            Node t = tail;
            node.setPrevRelaxed(t);         // avoid unnecessary fence
            if (t == null)
                tryInitializeHead();
            else if (!casTail(t, node))
                node.setPrevRelaxed(null);  // back out
            else
                t.next = node;
        } else if (first && spins != 0) {
    
    
            --spins;                        // reduce unfairness on rewaits
            Thread.onSpinWait();
        } else if (node.status == 0) {
    
    
            node.status = WAITING;          // enable signal and recheck
        } else {
    
    
            long nanos;
            spins = postSpins = (byte)((postSpins << 1) | 1);
            if (!timed)
                LockSupport.park(this);
            else if ((nanos = time - System.nanoTime()) > 0L)
                LockSupport.parkNanos(this, nanos);
            else
                break;
            node.clearStatus();
            if ((interrupted |= Thread.interrupted()) && interruptible)
                break;
        }
    }
    return cancelAcquire(node, interrupted, interruptible);
}

countDown方法

countDown方法是用于减少锁存器的计数,如果计数达到零,则释放等待线程。

public void countDown() {
    
    
    sync.releaseShared(1);
}

如果state=0,就会唤醒等待队列的第一个等待线程。tryReleaseShared方法会CAS修改state的值直到成功。

public final boolean releaseShared(int arg) {
    
    
    if (tryReleaseShared(arg)) {
    
    
        signalNext(head);
        return true;
    }
    return false;
}

如果传入的结点不为空,且next结点不为空,status状态不是0,修改next结点的状态,然后调用LockSupport.unpark解除线程的阻塞状态。

private static void signalNext(Node h) {
    
    
    Node s;
    if (h != null && (s = h.next) != null && s.status != 0) {
    
    
        s.getAndUnsetStatus(WAITING);
        LockSupport.unpark(s.waiter);
    }
}

猜你喜欢

转载自blog.csdn.net/u012734723/article/details/112691506