并发

ReentrantLock源码之解锁

2019-12-18  本文已影响0人  loveFXX

解锁过程调用ReentrantLock.unlock()方法

如果有三个线程t1、t2、t3。t1线程持有锁之后,t2、t3线程入队。加锁之后的AQS状态图如下:


image.png

t1线程调用ReentrantLock.unlock()方法,会调用下面过程。

unlock

ReentrantLock#unlock


image.png
release

AbstractQueuedSynchronizer#release


image.png
tryRelease

调用到AbstractQueuedSynchronizer的tryRelease()方法,具体实现ReentrantLock.Sync#tryRelease


image.png

exclusiveOwnerThread表示独占模式同步的当前拥有者。
这个方法,首先获取state减去releases的值1,并判断c是否等于0,如果不等于0则表示是重入锁需要多次释放。释放一次,通过setState(c)设置一次state的值。最终释放当前线程,state值是0并返回true

release
image.png

t1线程释放锁state改为0。if条件成立。把head节点赋值给h,并判断是否需要unpark唤醒线程。
首先需要了解当前队列的waitStatus值。根据t1、t2、t3线程当前的ASQ队列图显示。当前一直是t1线程持有,调用解锁方法之前。t2线程park之前,把head节点waitStatus设置为-1。t3线程park前,把t2线程waitStatus设置为-1。t3线程的waitStatus没有后续线程进入,是tail节点waitStatus的值是初始状态0。
解析判断条件h != null && h.waitStatus != 0
如果h = null则头结点为空说明AQS队列为空,没有需要唤醒的线程。h != null&&h.waitStatus != 0说明后续有在park的线程,所以需要唤醒。调用unparkSuccessor()方法

unparkSuccessor
image.png

当前头结点的waitStatus值是-1,首先通过compareAndSetWaitStatus方法把当前节点waitStatus设置为0。然后获取头结点的(next)下一个节点,调用 LockSupport.unpark(s.thread);方法唤醒这个线程所属的节点。即t2线程


image.png
unpark

LockSupport#unpark 调用UNSAFE的native方法


image.png
parkAndCheckInterrupt

AbstractQueuedSynchronizer#parkAndCheckInterrupt
唤醒t2线程,将会从调用park的地方继续执行(在加锁过程,加锁失败入队后)


image.png

检查isInterrupted标志位是否被改变。

acquireQueued

AbstractQueuedSynchronizer#acquireQueued 继续执行唤醒后的线程


image.png

释放t2线程的node节点


image.png
然后唤醒t3线程经历同样的过程。在释放t3线程的Node节点后由于t3的Node的waitStatus=0,不需要唤醒其他线程。

interrupt()方法

线程线程的Thread.interrupt()方法是中断线程。中断是在线程完成它的任务之前,停止它当前正在执行的操作。分以下几种情况:
1、线程阻塞在调用wait()、join()、sleep(long)、park()方法时,将会清除线程的中断状态并抛出InterruptedException异常
2、线程阻塞在InterruptibleChannel的IO上,Channel将会被关闭,线程被置为中断状态并抛出ClosedByInterruptException
3、线程阻塞在Selector,线程被置为中断状态,select方法会马上返回,类似Selector调用wakeup
4、如果不是以上三种情况,thread.interrupt()方法是设置线程的中断状态值。

 /**
     * Interrupts this thread.
     *
     * <p> Unless the current thread is interrupting itself, which is
     * always permitted, the {@link #checkAccess() checkAccess} method
     * of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     *
     * <p> If this thread is blocked in an invocation of the {@link
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
     * class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link
     * java.nio.channels.InterruptibleChannel InterruptibleChannel}
     * then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     *
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
     * then the thread's interrupt status will be set and it will return
     * immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     *
     * <p> If none of the previous conditions hold then this thread's interrupt
     * status will be set. </p>
     *
     * <p> Interrupting a thread that is not alive need not have any effect.
     *
     * @throws  SecurityException
     *          if the current thread cannot modify this thread
     *
     * @revised 6.0
     * @spec JSR-51
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

加锁方法lock

当调用了interrupt()方法
AbstractQueuedSynchronizer#acquireQueued方法返回interrupted=true


image.png

AbstractQueuedSynchronizer#acquire 调用selfInterrupt()方法


image.png
AbstractQueuedSynchronizer#selfInterrupt 再次调用interrupt()方法设置线程的中断状态值
image.png

加锁方法lockInterruptibly

当调用了interrupt()方法
ReentrantLock#lockInterruptibly


image.png
image.png

直接抛出InterruptedException中断异常


image.png

总结:

ReentrantLock重入锁的解锁过程。调用unlock解锁方法。把state值释放一次重新设置一次减1后的值。线程解锁完(state=0),根据AQS队列来判断是否需要释放下一个线程的Node节点,有下一个则调用unpark方法释放。线程继续从park的地方继续调用循环并重置head节点。
调用interrupt()方法,加锁方法lock和lockInterruptibly的区别

上一篇下一篇

猜你喜欢

热点阅读