Java并发编程(四)《锁原理》

Java并发编程(四)《锁原理》

@(并发)

4.1 队列同步器(AQS)

4.1.1 组件构成

  • 使用int型的成员变量标识同步状态
  • 使用内置的FIFO队列来完成获取锁状态失败的线程排队的工作.
  • 构成如图:
    Alt text

4.1.2 同步状态位

4.1.2.1 volatile修饰同步状态

/**
 * The synchronization state.
 */
private volatile int state;

/**
 * Returns the current value of synchronization state.
 * This operation has memory semantics of a <tt>volatile</tt> read.
 * @return current state value
 */
protected final int getState() {
    return state;
}

利用volatile语义,同步状态state一旦被修改,对于后续线程的读立马可见。

4.1.2.2 CAS设置锁状态位

/**
 * Atomically sets synchronization state to the given updated
 * value if the current state value equals the expected value.
 * This operation has memory semantics of a <tt>volatile</tt> read
 * and write.
 *
 * @param expect the expected value
 * @param update the new value
 * @return true if successful. False return indicates that the actual
 *         value was not equal to the expected value.
 */
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

存在多个线程竞争设置同步状态,用CAS保证状态的原子性。

4.1.3 CLH同步队列

4.1.3.1 数据结构

1. 获取同步状态失败的线程会构造成节点Node,以自旋CAS的方式加入到同步队列中。

2. 节点的数据结构如下:

Node {
        /**
         * 节点状态
         */
        int waitStatus;
        /**
         * 前驱节点
         */
        Node prev;
        /**
         * 后继节点
         */
        Node next;
        /**
         * 节点类型(独占 EXCLUSIVE/共享 SHARED)
         */
        Node nextWaiter;
        /**
         * 线程引用
         */
        Thread thread;
    }

3.节点属性说明:

keytypedesc
waitStatusint节点状态 :①INITIAL,值为0,标识初始归0状态;②SIGNAL,值为-1,表示有后继节点在等待,需要当前节点释放了同步状态通知后继节点;③CONDITION ,值为-2 ;④ PROPAGETE ,值为-3 ;⑤ CANCELLED,值为1,表示当前线程因中断或者超时被取消。
prevNode前驱
nextNode后继
nextWaiterNode独占 EXCLUSIVE常量/共享 SHARED常量
threadThread引用线程

4.1.3.2 独占式同步状态获取和释放

(1) 获取锁

1. 独占式获取锁流程分析:
Alt text

2. 代码分析:
(1) 加锁通过调用同步器的acquire()方法,该方法对中断不敏感,也就是线程获取同步状态失败加入同步队列中,后续对下线程中断,线程也不会从同步队列中移除。

/**
 * Acquires in exclusive mode, ignoring interrupts
 */
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

说明:
- tryAcquire( ) 线程安全的尝试获取同步状态。
- 若同步状态获取失败(同一时间只能有一个线程获取到同步状态),调用addWaiter( )构造独占式Node.EXCLUSIVE节点,以CAS方式串行地加入到同步队列中。
- 加入到同步队列中,调用acquireQueued( )以”死循环”的方式检查自身节点的前驱是否是头节点来获取同步状态,失败则线程进入等待状态,等待被唤醒。
- 节点获取到同步状态返回中断标识(清除了线程的中断位),若线程被中断则调用selfInterrupt( ),再次将当前线程中断标识位设置成中断.

(2) tryAcquire( ) 尝试获取同步状态

protected boolean tryAcquire(int arg) {
   throw new UnsupportedOperationException();
}

这里交给子类去实现,下面用公平锁举例:

/**
 * Fair version of tryAcquire.  Don't grant access unless
 * recursive call or no waiters or is first.
 * 公平锁:只有头节点(获取到同步状态的节点)的next节点最先获取同步状态,不用与不在同步队列中的线程竞争同步状态。
 */
protected final boolean tryAcquire(int acquires) {
    //final防止当前前程被篡改
    final Thread current = Thread.currentThread();
    //同步状态state用volatile修饰,从主存中获取
    int c = getState();
    //若同步状态为0,当前没有线程占有锁,可获取锁
    if (c == 0) {
        //判断同步队列不为空并且头结点的next节点的线程!=当前线程,表示有线程比当前线程更早的请求获取锁,这样为了公平不获取锁,而让当前线程加入到同步队列尾部等待。
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            //CAS设置锁状态成功的,设置同步器锁状态被独立占有的线程,返回获取锁成功true。
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //若同步状态为0,当前有线程占有锁,下面通过(当前线程==占有锁的线程)来判断是不是重入锁
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

(3) addWaiter( ) 线程获取同步状态失败,构建节点加入同步队列

/**
 * 构建独占式节点
 */
addWaiter(Node.EXCLUSIVE);
/**
 * 创建节点入队
 */
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    //快速尝试在尾部添加当前节点
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

/**
* 插入节点
* CAS串行的方式将当前节点加入到尾部,若队列为空初始化
*/
private Node enq(final Node node) {
   //自旋,compareAndSet失败,从头循环,拿最新的值
   for (;;) {
       Node t = tail;
       //若队列为空,new一个节点设置成头节点初始化,重新循环
       if (t == null) { // Must initialize
           if (compareAndSetHead(new Node()))
               tail = head;
       } 
       //若队列不为空,CAS设置当前节点为尾节点,失败重新循环,重试
       else {
           node.prev = t;
           if (compareAndSetTail(t, node)) {
               t.next = node;
               return t;
           }
       }
   }
}

(4) acquireQueued( ) 节点加入同步队列后,开始自旋获取同步状态。

/**
* 节点加入同步队列后,开始自旋获取同步状态
* 节点获取到同步状态会返回,同时返回中断标识
*/
final boolean acquireQueued(final Node node, int arg) {
    //final修饰当前节点,防止被篡改
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            //判断前驱是头节点,尝试获取同步状态
            if (p == head && tryAcquire(arg)) {
                //获取同步状态成功
                //将当前节点设置成头节点(因为独占式,获取到锁的只有一个线程,所以这里设置头不用CAS)
                setHead(node);
                //将老的header的next断开
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //前驱不是头节点或者没有获取到同步状态的,先判断可以进入等待么,若可以则线程进入等待状态,否则自旋,再次检查前驱是否为头节点并且尝试获取锁
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            //取消节点
            cancelAcquire(node);
    }
}

/**
* 根据前驱节点的状态判断当前节点是否应该进入等待状态
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        //前驱节点已经被标记过SIGNAL,标识前驱释放同步状态会通知当前节点的,所以当前节点可以安全进入等待。
        return true;
    if (ws > 0) {
        //前驱节点已经被取消,向前遍历找出没有被取消的节点,设置成当前节点的前驱,重新自旋尝试获取同步状态,不进入等待。
        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.
         */
         /*
         * 前驱节点状态是INIT/PROPAGATE的,设置前驱节点状态为SIGNAL,标识前驱释放锁需要通知当前节点。
         * 当前线程不进入等待,而是重试获取同步状态,保证前驱节点设置为SIGNAL时,前驱还没有释放锁,这样前驱释放锁
         * 时,可以根据status=SIGNAL通知当前节点结束等待
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

/**
 * 线程进入等待状态,唤醒后会返回线程的中断位
 * @return {@code true} if interrupted
 */
private final boolean parkAndCheckInterrupt() {
    //线程进入等待状态,若LockSupport已经提前unpark了(前驱同步状态提前释放了),这里会立即返回,不会阻塞等待。
    LockSupport.park(this);
    return Thread.interrupted();
}
(2) 响应中断获取锁
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    //线程被中断,当前线程中止获取锁
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //LockSupport.park(),响应中断,这里立马抛异常,线程执行到这里立马结束。
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

原理 :
利用LockSupport.park() 响应中断,但不会抛出InterruptedException,从park返回时候通过Thread.interrupted( )判断当前线程是否被中断,抛出InterruptedException,这样当前线程获取锁的行为立马停止。

(3) 超时获取锁
/**
 * 支持超时方式获取同步状态
 */
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}
/**
 * 规定的时间内获取锁,若超时则返回获取锁失败
 */
private boolean doAcquireNanos(int arg, long nanosTimeout)
    throws InterruptedException {
    //获取当前时间的纳秒数
    long lastTime = System.nanoTime();
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            //剩余的等待纳秒数<=0,不在尝试等待获取锁,直接返回false,获取锁失败
            if (nanosTimeout <= 0)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                //LockSupport超时等待,等待nanosTimeout后立马返回,不在等待
                //当然也可以在nanosTimeout内提前被唤醒,返回
                LockSupport.parkNanos(this, nanosTimeout);
            long now = System.nanoTime();
            //重新计算剩余的等待时间
            //剩余要等待的时间公式:nanosTimeout =nanosTimeout - 已经等待的时间
            nanosTimeout -= now - lastTime;
            lastTime = now;
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

超时获取同步状态的过程实际上是响应中断获取同步状态的增强版

(4) 释放锁

1.独占式释放锁流程分析:
Alt text
2.代码分析 :

/**
 * Releases in exclusive mode.
 */
public final boolean release(int arg) {
    //1.尝试释放同步状态
    if (tryRelease(arg)) {
        Node h = head;
        //2.若释放成功,则唤醒在等待中的后继节点
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

/**
 * 尝试释放同步状态
 */
protected final boolean tryRelease(int releases) {
    //获取同步状态减一
    int c = getState() - releases;
    //若当前线程不是获取同步状态的线程就抛异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //可重入锁需要释放多次,同步状态才能归0,被其他线程获取
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //独占式释放锁,只有一个线程操作,无需CAS防止并发
    setState(c);
    return free;
}

/**
 * 若存在进入等待状态的后继,需要unpark唤醒它
 */
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)
        //这里CAS清空状态,CAS设置为初始状态,若此时别的线程修改了状态,CAS失败也没事
        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);
}
(5) 公平锁和非公平锁
  • 在Java的ReentrantLock构造函数中提供了两种锁:创建公平锁和非公平锁(默认)。
  • 在公平的锁上,线程按照他们发出请求的顺序获取锁,但在非公平锁上,则允许‘插队’:当一个线程请求非公平锁时,如果在发出请求的同时该锁变成可用状态,那么这个线程会跳过队列中所有的等待线程而获得锁。 非公平的ReentrantLock 并不提倡 插队行为,但是无法防止某个线程在合适的时候进行插队。在公平的锁中,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个所,那么新发出的请求的线程将被放入到队列中。而非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中。
  • 非公平锁性能高于公平锁性能的原因:
    (1)在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。
    (2)假设线程A持有一个锁,并且线程B请求这个锁。由于锁被A持有,因此B将被挂起。当A释放锁时,B将被唤醒,因此B会再次尝试获取这个锁。与此同时,如果线程C也请求这个锁,那么C很可能会在B被完全唤醒之前获得、使用以及释放这个锁。这样就是一种双赢的局面:B获得锁的时刻并没有推迟,C更早的获得了锁,并且吞吐量也提高了.
    (3) 当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁。在这些情况下,插队带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)可能不会出现。

4.1.3.3 共享式同步状态获取和释放

共享式与独占式的区别在于,共享式允许同一时间可以多个线程获取到锁。

(1) 获取锁
/**
* 共享式获取同步状态
* Acquires in shared mode, ignoring interrupts.  Implemented by
* first invoking at least once {@link #tryAcquireShared},
* returning on success.  Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquireShared} until success.
*
* @param arg the acquire argument.  This value is conveyed to
*        {@link #tryAcquireShared} but is otherwise uninterpreted
*        and can represent anything you like.
*/
public final void acquireShared(int arg) {
     //tryAcquireShared < 0 表示获取不到同步状态
     if (tryAcquireShared(arg) < 0)
         doAcquireShared(arg);
 }
/**
 * 获取同步状态失败后,构建共享型节点加入同步队列,自旋、等待
 * Acquires in shared uninterruptible mode.
 * @param arg the acquire argument
 */
private void doAcquireShared(int arg) {
    //构建共享型节点 final修饰,防止变量被篡改重新赋值
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        //自旋获取同步状态
        for (;;) {
            final Node p = node.predecessor();
            //前驱节点是头节点最先尝试获取共享锁
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    /**
                     * 当前节点获取到共享锁,设置头结点为当前节点并且要将尝试获取共享锁的行为要向后传播.
                     * 此时可能多个线程释放了共享锁,也就是多个后续等待的线程可以获取到共享锁,
                     * 因此,当前节点获取到共享锁后需要同时通知后继竞争共享锁,通知更多的线程尝试获取锁。
                     */
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
/**
 * 当前节点获取到共享锁,设置头结点为当前节点并且要将尝试获取共享锁的行为要向后传播
 * Sets head of queue, and checks if successor may be waiting
 * in shared mode, if so propagating if either propagate > 0 or
 * PROPAGATE status was set.
 *
 * @param node the node
 * @param propagate the return value from a tryAcquireShared
 */
private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    //1.设置头结点为当前节点
    setHead(node);
    /*
     * Try to signal next queued node if:
     *   Propagation was indicated by caller,
     *     or was recorded (as h.waitStatus) by a previous operation
     *     (note: this uses sign-check of waitStatus because
     *      PROPAGATE status may transition to SIGNAL.)
     * and
     *   The next node is waiting in shared mode,
     *     or we don't know, because it appears null
     *
     * The conservatism in both of these checks may cause
     * unnecessary wake-ups, but only when there are multiple
     * racing acquires/releases, so most need signals now or soon
     * anyway.
     */
     //2.当前节点设置成head后,要唤醒共享型next节点
    if (propagate > 0 || h == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

/**
 * 共享模式,唤醒头结点的后继节点
 * 释放共享锁时也会用到次方法
 * Release action for shared mode -- signal successor and ensure
 * propagation. (Note: For exclusive mode, release just amounts
 * to calling unparkSuccessor of head if it needs signal.)
 */
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;
            /*
             * 头结点状态为SIGNAL,表示后面有节点进入了等待状态,需要被通知唤醒。
             * 通知后继时,这里头结点状态要归0。
             * 这里存在并发,若CAS更新失败,别的线程改了状态,重新检查节点状态
             */
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            /*
             * 这里当头节点状态为0时,CAS设置头节点状态为PROPAGATE
             * 这样做在高并发的情况下,可以避免很多无意义的通知,unparkSuccessor(h)
             * 比如此时多个线程释放共享锁,需要不停地通知头节点的后继,其实只要通知一次后继就好。
             */
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
         /*
         * 同步器head是volatile变量,改变了值,下一次读取会立马知道
         * 这头节点还没有发生变化,方法结束
         */
        if (h == head)                   // loop if head changed
            break;
    }
}
(2) 释放锁
/**
 * 共享模式,释放共享锁
 * 因为多个线程获取了共享锁,所以释放锁存在多个线程并发.
 * Releases in shared mode.  Implemented by unblocking one or more
 * threads if {@link #tryReleaseShared} returns true.
 *
 * @param arg the release argument.  This value is conveyed to
 *        {@link #tryReleaseShared} but is otherwise uninterpreted
 *        and can represent anything you like.
 * @return the value returned from {@link #tryReleaseShared}
 */
public final boolean releaseShared(int arg) {
    //尝试释放共享锁
    if (tryReleaseShared(arg)) {
        //释放成功,唤醒头结点的后继节点
        doReleaseShared();
        return true;
    }
    return false;
}

4.1.3.4 独占式和共享式获取锁区别

  • 独占式同一时间只能允许一个线程获取锁;共享式允许多个线程获取锁。
  • 独占式锁状态为0,表示可以获取锁为1表示锁被占用;而共享式锁状态>=0表示可以获取锁<0表示获取不到锁。
  • 独占式,头结点后继节点获取到锁状态后,只要将后继设置成头就好;而共享式头结点后继节点获取到锁状态后,设置头节点为后继节点,并且同时唤醒后继节点的next节点,将尝试获取共享锁的行为传递下去(因为共享锁可以多个线程获取).

4.1.4 等待队列

4.1.4.1 等待机制

1. 线程进入等待队列的流程:
Alt text

2. 代码分析:

/**
 * Implements interruptible condition wait.
 * 线程主动进入等待状态,释放锁,等待别的线程被唤醒
 * 响应中断,若线程在等待状态中被中断,这里线程立即醒来
 */
public final void await() throws InterruptedException { 
    if (Thread.interrupted())
        throw new InterruptedException();
    /**
     * 1.构造CONDITION节点,加入到等待队列尾部
     */
    Node node = addConditionWaiter();
    /**
     * 2.释放锁
     */
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    /**
     * 3。线程进入等待状态,等待被通知唤醒
     */
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    /**
     * 4。线程被通知唤醒,尝试竞争锁
     */
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    //线程被中断,抛出异常.
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

4.1.4.2 通知机制

1. 线程进通知等待线程的流程:
Alt text

2. 代码分析:

/**
* 通知唤醒等待队列中最长等待的线程,即头结点。
* 若存在,则将其从等待队列头部移动到等待队列尾部,尝试竞争锁
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
*         returns {@code false}
*  
*/
public final void signal() {
   /**
    * 1。查看当前线程是不是占有锁的线程
    */
   if (!isHeldExclusively())
       throw new IllegalMonitorStateException();
   Node first = firstWaiter;
   /**
    * 2。唤醒等待队列头结点
    * 将其从等待队列头部移动到等待队列尾部
    * 按条件unpark头节点引用线程。
    */
   if (first != null)
       doSignal(first);
}

4.2 LockSupport 工具

4.2.1 原理

LockSupport的park实际上是让当前线程进入等待状态,挂起,而不是如synchronized使线程进入阻塞状态。

4.2.2 API

方法描述
public static void park()阻塞当前线程,只有调用unpark(Thread thread)或者当前线程被中断,才能park()返回
void parkNanos(long nanos)阻塞当前线程,最多不超过nanos纳秒,在park()基础上增加超时返回
void parkUntil(long deadline)阻塞当前线程,直到某个时间点,从1970年到deadline的毫秒数
void unpark(Thread thread)唤醒处于阻塞状态的线程

4.2.3 park和unpark的触发先后顺序

  • park阻塞住当前线程,然后在调用unpark,当前线程会唤醒。
  • 先unpark当前线程,然后在park阻塞当前线程,当前线程不会被阻塞,立马从返回park方法返回.

4.2.4 LockSupport.park() 能响应中断

  • LockSupport.park() 能响应中断,但不会抛出InterruptedException,需要通过Thread.interrupted( )判断当前线程是否被中断。
  • Object.wait() .Object.join(), Object.sleep() 响应中断 会抛出InterruptedException

4.2.5 LockSupport.park() 不能重入

LockSupport.park 不能重入,再次调用park,会一直阻塞下去

4.3 锁的实例

4.3.1 重入锁ReentrantLock

4.3.2 读写锁ReentranReadWriteLock

锁降级、锁降级

4.4分布式锁

4.4.1基于ZK的Menagerie

4.5 惊群效应

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值