JUC——ReentrantLock源码简读(中)

7 篇文章 1 订阅

JDK1.8

前言

《JUC——ReentrantLock源码简读(上)》提到会在这篇讨论hasQueuedPredecessors() 方法,为了方便看,再来贴下源码和上篇写的注释:

package java.util.concurrent.locks;

public class ReentrantLock implements Lock, java.io.Serializable {
	// ....忽略很多源码
	
	/**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
       
        /**
        * tryAcquire()返回false,需要入队,有这几种情况:
        * 	1. 锁被其他线程持有,并且不是重入情况
        * 	2. 锁还没被其他线程持有,但是队列中还有其他等锁的线程
        * 	3. 锁还没被其他线程持有,队列中也没有其他线程等锁,但是CAS操作失败
        */
        protected final boolean tryAcquire(int acquires) {
        	// 5.1 获取当前线程
            final Thread current = Thread.currentThread();
            
            /**
            * 5.2 这个getState获取的是java.util.concurrent.locks.AbstractQueuedSynchronizer
            * 类下的state变量的值,当有线程获取到锁时,这个值加1,
            * 等于0表示当前还没有线程获取到锁
            */ 
            int c = getState();
            if (c == 0) {
            	/**
            	* 5.3 当前这个线程判断到state等于0,但是并不代表它就能获取到锁,因为
            	* 有可能队列中还有等锁的线程,只是这个线程来的时候,持有锁
            	* 的线程刚好释放锁而已。所以,hasQueuedPredecessors()方法就是
            	* 去判断是否要把当前线程加入队列
				* 
				* 如果hasQueuedPredecessors()返回fale,则进行CAS操作
				* 如果hasQueuedPredecessors()返回true,tryAcquire()返回false,进行入队操作
            	*/
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    // 把当前线程设置为持有锁的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
            	/**
            	* 5.3^ 如果当前线程等于持有锁的线程,说明是线程重入锁,则把state值加1
            	* (ReentrantLock是可重入锁)
            	*/
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
}

hasQueuedPredecessors()方法源码:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    // ....忽略很多源码
	
	public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
}

公平锁——hasQueuedPredecessors()

在公平锁tryAcquire()方法中,如果当前锁的状态是0(即没有线程持有锁),当前线程首先会去执行hasQueuedPredecessors()方法判断自己要不要排队,有好几种情况。

情况1

t1来了,队列还没有被初始化

h!=t -> false

整个方法返回false,可以去执行CAS操作

情况2

t1持有锁,t2加入等锁队列后执行acquireQueued(),由于它是第一个等锁线程,会进行自旋操作。t2第一次自旋之前,t1把锁释放了,队列中是这种情况:
在这里插入图片描述
t2执行自旋进入hasQueuedPredecessors()方法:

h!=t -> true
s = t2
s == null -> false
s.thread != Thread.currentThread()) -> false

整个方法返回false,可以去执行CAS操作

情况3

t1持有锁,t2在队列中等锁,队列中是这种情况:
在这里插入图片描述
然后t1把锁释放了,把state改成0,thread改成null,但还没来记得及唤醒t2,或者t2被唤醒了,但是此时t3来了先去执行tryAcquire()进入hasQueuedPredecessors()方法:

h != t -> true
s = t2
s == null -> false
s.thread != Thread.currentThread()) -> true

整个方法返回true,t3直接入队列,这就体现了公平锁,即使t3先进入到tryAcquire()方法中,但是还是拿不到锁,只能让给t2

情况4

t3拿到锁在执行,队列中是这种情况:
在这里插入图片描述
然后t3释放锁,把state改成0,thread改成null,此时t4来了,执行tryAcquire()进入hasQueuedPredecessors()方法:

h != t -> false

整个方法返回false,t4可以执行CAS操作

情况5

假设此时队列的状态是t1拿到锁,t2进入队列(还没开始自旋),然后t1释放锁,
在这里插入图片描述
此时t3来了,执行tryAcquire()hasQueuedPredecessors()方法:

h != t -> true

刚执行完上面那不,还没往后执行的,CPU时间片被t2抢去了,t2执行第一次自旋:

h != t -> true
s = t2
s == null -> false
s.thread != Thread.currentThread()) -> false

整个方法返回false,t2执行CAS操作成功后获取锁,并修改队列状态:
在这里插入图片描述
然后t3继续执行:

// h != t -> true 这是之前执行的 
// 此时h变成了t2
s == null -> true

整个方法返回ture,t3入队。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值