Java 多线程-2 ReentrantLock原理

三. 原理

1. lock()

我们先以 ReentrantLock.lock() 来看简单加锁的场景;

// ----------------------------------- ReentrantLock ----------------------------------------
public void lock() {
    sync.lock();
}




// ----------------------------------- FairSync ----------------------------------------
final void lock() {
    // 调用 acquire(1)
    acquire(1);
}


// ------------------------------- AbstractQueuedSynchronizer ----------------------------------
public final void acquire(int arg) {
    // 1. 先执行 tryAcquire(arg) 尝试获取锁
    //	获取到了锁返回 true,直接退出该方法
    //	没有获取到锁返回 false, 调用 acquireQueued(),表示将当前线程加入阻塞队列阻塞等待获取锁
    
    // 2. acquireQueued() 表示将当前线程加入阻塞队列阻塞等待获取锁
    //	正常获取到锁返回 false,直接退出该方法
    //	如果外部中断了该线程,也会获取到锁,但是返回 true,执行方法体里的 Thread.currentThread().interrupt();
    //	了解中断即可
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        Thread.currentThread().interrupt();
}

主要是 tryAcquire(arg) 和 acquireQueued(),我们重点看下这两个方法做了啥;

1.1 tryAcquire(arg)

tryAcquire(arg) 如下;

// ----------------------------------- ReentrantLock ----------------------------------------
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 1. 如果当前没有等待的线程,CAS 获取锁
        // 获取锁成功的话讲当前线程设置为 exclusiveOwnerThread,表示独占该锁
        // 并返回 true,表示获取了锁
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    
    // 2. state != 0,表示有线程持有了锁
    // 由于 ReentrantLock 是可重入锁,检查持有锁的线程是否是当前线程
    // 如果是的话 state++,并返回 true,表示获取到了锁
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    
    // 3. 没有获取到锁,返回 false
    return false;
}

1.2 acquireQueued()

acquireQueued(),该方法表示以不可中断的方式加了阻塞等待队列,可以看到线程中断不影响它获取到锁;

// ------------------------------- AbstractQueuedSynchronizer ----------------------------------
/**
 * 以不可中断的方式加了阻塞等待队列
 *
 * @param node the node
 * @param arg the acquire argument
 * @return {@code true} 如果在等待期间发生了中断返回 true,没有发生中断的话返回 false
 */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 1. 死循环自旋
        //	除非线程放弃获取锁,正常情况下一定能获取到锁
        for (;;) {
            final Node p = node.predecessor();
            
            // 2. 如果当前节点是头结点的后继节点,尝试获取锁,并返回 interrupted 值
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null;
                failed = false;
                return interrupted;
            }
            
            // 3. 将当前节点加入阻塞队列阻塞,直到被唤醒
            // parkAndCheckInterrupt() 会执行 LockSupport.park(this);
            //	正常唤醒 LockSupport.unpark() 时,方法返回 false,interrupted 还是 false
            //	如果是中断唤醒,方法返回 true,进入方法体,interrupted = true
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}



// ------------------------------- AbstractQueuedSynchronizer ----------------------------------
private final boolean parkAndCheckInterrupt() {
    // 1. 阻塞挂起当前线程
    LockSupport.park(this);
    
    // 正常唤醒 LockSupport.unpark() 时,返回 false
    // 如果是中断唤醒,返回 true,并情况中断标示位,使 Thread.isInterrupted() == false
    return Thread.interrupted();
}

2. unlock()

unlock() 表示释放锁,我们以公平锁来看下 unlock();

// ----------------------------------- ReentrantLock ----------------------------------------
public void unlock() {
    // 执行 sync.release(1)
    sync.release(1);
}




// ------------------------------- AbstractQueuedSynchronizer ----------------------------------
public final boolean release(int arg) {
    // 尝试释放锁
    // 完全释放了锁的话,唤醒阻塞队列中的后继线程,并返回 true
    // 没有完全释放锁,返回 false
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

2.1 tryRelease(releases)

我们知道 ReentrantLock 是可重入锁,释放锁需要使 state == 0 才算完全释放锁;

// ----------------------------------- ReentrantLock ----------------------------------------
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    
    // 1. 注意!!!
    //	如果当前线程没有持有锁,调 lock.unlock() 走到这里会抛出 IllegalMonitorStateException 异常 
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        
        // 2. 完全释放掉锁,返回 true
        //	没有完全释放掉锁,返回 false
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

3. tryLock()

tryLock() 如下;

// ----------------------------------- ReentrantLock ----------------------------------------
public boolean tryLock() {
    // 以不公平锁的方式获取锁 
    return sync.nonfairTryAcquire(1);
}

3.1 nonfairTryAcquire(acquires)

Sync 的 nonfairTryAcquire(acquires) 如下:

// ------------------------------------- Sync ------------------------------------------
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 1. 直接尝试去获取锁,获取锁成功的话返回 true
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    
    // 2. 竞争锁失败,看当前线程是否已经持有锁
    // 是的话 state++,返回 true
    // 否的话返回 false,竞争锁失败
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

4. tryLock(timeout)

ReentrantLock 的 tryLock(timeout) 如下,它会抛出受检异常 InterruptedException,说明和中断有关;

// ----------------------------------- ReentrantLock ----------------------------------------
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

4.1 tryAcquireNanos(timeout)

// ----------------------------------- AbstractQueuedSynchronizer -------------------------------
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
    throws InterruptedException {
    // 1. 如果线程已经中断了,直接抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    
    // 2. 先尝试直接获取锁,调用 tryAcquire(arg)
    // 获取锁失败的话进入阻塞等待队列中挂起 timeout 时间,期间可能会被唤醒获取锁,调用 doAcquireNanos()
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}




private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        
        // 1. 死循环自旋
        for (;;) {
            
            // 2. 如果当前节点是头结点的后继节点,尝试获取锁,返回 true,表示获取到锁
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null;
                failed = false;
                return true;
            }
            
            nanosTimeout = deadline - System.nanoTime();
            
            // 3. 如果 deadline 已到,返回 false,表示获取锁失败
            if (nanosTimeout <= 0L)
                return false;
            
            // 4. 当前线程进入阻塞等待队列中等待 timeout 时间
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            
            // 5. 需要比较 Thread.interrupted() 的值
            // 如果线程被中断了,该值为 true,并往外抛出异常
            // 其他情况,正常唤醒、parkNanos() 时间到,该值都是 false
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

至此,ReentrantLock 分析完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值