Common components in Java Concurrency

lock

ReentrantLock

Rlock at construction time passing a flag is boolean fair, showing the lock when the release whether the authority will tend to wait the longest thread, otherwise makes no guarantee order to obtain the lock. Lock fair will bring significant performance loss, but the advantage of a smaller acquisition lock time variance, without the risk of starvation.

The call tryLock Rlock () method when not care whether a fair mark, this time as long as no thread holds the lock, then you can get into the lock, do not care about those threads are waiting lock. tryLock () inherited from the Lock class, if not at this time to acquire the lock, it returns false directly, without blocking threads running.

RLock inner class inherits Sync AQS class, semantic lock through this class. Represents the case where the number of threads holding the lock (reentrant) with members of Sync state. Sync whether fair subclass includes two versions, a non-default fair.

Rlock difference in the lock () and lockInterruptibly () that thread is blocked while waiting for the lock responds interrupted. If another thread has interrupted this thread to get this thread to acquire a lock, lockInterruptibly () call end, thread stops blocking.

By calling sync.nonfairTryAcquire tryLock () method (1) to achieve. Because this method is simply to determine if there are other thread holds the lock, any direct returns false, otherwise if state = 0 (no thread holding the lock) or current == getExclusiveOwnerThread () (This thread is held), to directly modify the state status, and then return to true. code show as below:

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
复制代码

Here mainly to see the realization of lock methods. lock () method by calling sync.lock (). Look at the situation fair. (R) lock () -> (R) sync.lock () -> (A) acquire (1) -> (A)

public final void acquire(int arg) {
        //如果tryAcquire成功,后面自然不用执行,否则排队(所以线程在队列中只会有一个节点)
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
复制代码

We guess, acquireQueued thread enters the blocking state. First look at a step by step, the first step is addWaiter;

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
复制代码

In addition to pay attention to use the Node stuffed cas way to the end of the list, other seemingly nothing to depth, skip. After the end of this step we have successfully created a representative of Node this thread to the tail of the queue, then what?

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
复制代码

In acquire method, we have seen once tryAcquire () method, we assumed it was false for the situation, and then continue to go back, now face, it seems necessary to look at its implementation. Surprisingly, tryAcquire () method is an abstract AQS inside, we need to realize themselves. Above starting from tryLock (), we have seen a nonfairTryAcquire () method, and from the point of view of this method is the name regardless of whether it is fair, to acquire a lock, success true, failure false, then tryAcquire () how to achieve it?

tryAcquire achieved under NonfairSync

protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
复制代码

tryAcquire achieved under FairSync

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //这儿就是判断是否fair的关键代码,先判断是是否在排队呢
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
复制代码

Although the lock () method have come to the following tryAcquire Sync class method, but could see a different sub-class Sync method to achieve this is not the same. That was back acquireQueued approach it,

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //是这里让线程阻塞了
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //可以直接把interruptedException抛出来,这就是lockInterupt()方法的做法
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
复制代码

Look at this method

/**
     * Checks and updates status for a node that failed to acquire.
     * Returns true if thread should block. This is the main signal
     * control in all acquire loops.  Requires that pred == node.prev.
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
复制代码

The purpose of this method are mainly two

  1. Tell a node, release the lock when the need to inform me (processor's status is set to Singal)
  2. The nodes of the thread occupied cancelled cancel

Preparatory work done above, this thread can feel at ease "rest" of the.

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        //注意,这里检测了线程是否被interrupted。如果此时捕获到中断的信息,需要在上面代码中“抛出”此中断,让其他实现中断响应策略的代码,能够获知这一状态。
        return Thread.interrupted();
    }
复制代码

Now regarded as a thread into the blocked. When awakened? Look unLock () code:

public void unlock() {
        sync.release(1);
    }
复制代码

Code Link is (R) unlock () -> (A) release () -> (R) tryRelease ()

protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
复制代码

This operation is substantially the opposite tryAcquire. If the release is successful, put the queue thread releases the first node, the code:

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }
复制代码

synchronized

What synchronized yes?

CountDownLatch

CDL for a thread to wait for another thread to complete the above series of operations. Upon initialization specify a count, each operation is completed calls countDown () to reduce the count. When reducing the count to 0 when all blocked in await () method above thread will execute immediately. Note count can not be reset, if you want to reset, it is recommended to use CyclicBarrier.

CDL can help ensure memory consistency principle: a thread calls the countdown () method must have occurred before the action in the other thread await before the method returns ().

And achieve RLock like, CDL also inherited the internal AQS categories, namely Sync class. CDL await () Process Analysis: (C) await () -> (A) acquireSharedInterruptibly

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
复制代码

And RLock Similarly, first try to obtain it locks with tryAcquireShared

protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
复制代码

When initializing the CDL, the incoming state is specified number count, so tryAcquireShared this way is to look at whether the state reduced to zero, if reduced to zero, would not have blocked the execution down, otherwise it will be called the AQS doAcquireSharedInterruptibly method:

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);
        }
    }
复制代码

We found this method and acquireQueued method similar to the above-mentioned basic

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //是这里让线程阻塞了
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //可以直接把interruptedException抛出来,这就是lockInterupt()方法的做法
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
复制代码

The difference is that when the precursor of this node is head node (meaning that he is now the first row), the call is tryAcquireShared method, more than one shared tryAcquire. Achieve tryAcquire methods are generally relatively simple, and in fact almost tryAcquire mean, this is a little complicated in this state, in addition to other parts of the node status is set to SHARED, and above it makes no difference, followed by blocking the same logic.

Then look countdown () method.

public void countDown() {
        sync.releaseShared(1);
    }
复制代码

And then go into the AQS


public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
复制代码

CDL, the state will be 1 until you return true to 0, the thread will be released at this time

protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
复制代码
private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
复制代码

Semaphore

S for some internal license, a thread is blocked not obtain license when a license until assigned to it. Release license when the licenses will increase in the S, therefore, S is typically used to control the number of threads to access a resource. S can have only one license during initialization, that is binary Semaphore, different roles and lock it, because the need to lock this thread releases the lock yourself, and bs not have this requirement.

Semaphore also divided whether it is fair, meaning RLock same.

Semaphore also implements the initial internal state of a subclass of the AQS Snyc.Sync (state) is the number of permit, represents the number of permit available. S look acquire method of:

//注意,semaphore的acquire默认是可以中断的,如果要不中断可以调用acquireUninterruptibly()
public void acquire() 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);
    }
复制代码

RLock looks like, acquire when the first try it, if successful the best, otherwise

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);
        }
    }
复制代码

And CDL like this, all nodes SHARED mode.

As can be seen from the above examples, in order to customize the concurrent components, you need to do a few things

  1. Internal succession AQS class, which is used to provide queue, blocking function
  2. If you need to synchronize concurrent components, first try by try (state) it can not get a lock, everything will be fine test is successful, the thread continues. If you do not succeed try, by AQS this thread added to the queue, while blocking its execution.
  3. Concurrent components if you want to release the lock, the first tryRelease AQS of state and other information changes, such as reducing the number of re-entry, modification to the results we want (such as RLock for 0, CDL in to 0), it will be by AQS node release, while unblock threads.

Most custom logic is that when we need to block a thread (insert node), that is what we need to try when it returns false, that is not such getState 0? Similarly, when we need to make getState 0?

How we are to achieve a synchronization tools?

public class MyThreadSyncUtil {
    private Sync sync;
    class Sync extends AQS {
        booleean tryAcquire();
    }
    
    //会阻塞,还可以区分是否响应中断,区别在于调用sync中响应中断的acquire,区别是此时park完线程以后判断是否抛出intrupted异常
    public void acquire() {
        sync.acquire(); // -> 这里面会用到我们刚刚实现的try,try成功就返回,否则将节点加入到队列
    }
    //直接返回
    public boolean tryAcquire(){
        sync.tryAcquire();
    }
}
复制代码

CyclicBarrier

CB scene using a series of threads waiting on each other behind the task to continue after the completion of the task. cyclic means that it can be reused in a later release of the thread. CB support passing a Runnable, indicate when all the threads to complete the task (but not yet released) to perform it.

After completion of each thread to perform their tasks, call cyclicBarrier.await () method, and block all the tasks to complete. await code:

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
复制代码

Mainly code dowait:

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;
            //如果barrier被破坏,直接抛出异常结束
            if (g.broken)
                throw new BrokenBarrierException();

            //如果这个线程中途被中断,会中断barrier
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;
            //最后一个执行的线程需要执行以下barrierCommand
            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) {
                    //阻塞的时候如果被中断,先结束barrier再抛出异常
                    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();
                    }
                }
                //有可能中途被唤醒(因为broken),判断出错以后抛出异常
                if (g.broken)
                    throw new BrokenBarrierException();
                //有可能更新到下一代
                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }
复制代码

breakBarrier code is

private void breakBarrier() {
        //broken这个状态属于generation
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }
复制代码

Guess you like

Origin juejin.im/post/5e674831f265da57701444bc