【java-jdk1.8 ReentrantLock源码解析】

3 篇文章 0 订阅

前言

一、ReentrantLock是什么?

二、解析源码的示例代码

三、源码解析

3.1 ReentrantLock的类图

3.2 ReentrantLock的构造方法

3.3 进入lock方法,以非公平锁来讲解

        3.4 进入acquire方法

        3.5 进入addWaiter方法

        3.6 进入enq方法

        3.7 进入acquireQueued方法

         3.8 进入unlock方法

         3.9 进入unparkSuccessor方法,释放锁

四、总结


前言

文章的目的:帮助对ReentrantLock感兴趣的人,分析ReentrantLock源码,共同进步。

以它的使用开始,介绍ReentrantLock的原理,分析每个方法的用处。

这是我的"第一个CNDS文章",有错误的地方,欢迎指出,谢谢!

哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈


一、ReentrantLock是什么?

ReentrantLock的作者:Doug Lea,它的作用和Synchronized一样,是为了保证java的原子操作。

但是他们的原理不一样:

Synchronized是JVM层面实现的,需要调用操作系统提供的互斥锁方法。

ReentrantLock是java提供的一个工具类,是API层面的。

二、解析源码的示例代码

import java.util.concurrent.locks.ReentrantLock;

public class TestDemo {
    static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        test1();
    }
    private static void test1() {
        try{
            // 加锁,AbstractQueuedSynchronizer的state属性+1,默认0
            reentrantLock.lock();
            test2();
        }catch (Exception e){

        }finally{
            // 释放锁,AbstractQueuedSynchronizer的state属性-1,当为0时,才去唤醒其他线程
            reentrantLock.unlock();
        }
    }
    private static void test2() {
        try{
            // 加重入锁---->第二次及以上,在加锁就state+1就行了,不需要再次获得锁
            reentrantLock.lock();
        }catch (Exception e){
        }finally{
            // 释放重入锁---->state-1
            reentrantLock.unlock();
        }
    }
}

三、源码解析

3.1 ReentrantLock的类图

Sync是ReentrantLock的一个静态内部类,继承了AbstractQueuedSynchronizer类,

AbstractQueuedSynchronizer是一个抽象的队列同步器,有很多实现用来实现lock锁的方法和属性。

3.2 ReentrantLock的构造方法

public ReentrantLock() {
    // 如果不传参数,默认sync属性是是一个非公平锁
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    // 如果传参数,为true就是公平锁,false为非公平锁
    sync = fair ? new FairSync() : new NonfairSync();
}

3.3 进入lock方法,以非公平锁来讲解

public void lock() { //当前类:ReentrantLock
    // 进入非公平锁NonfairSync的lock方法
    sync.lock();
}

final void lock() { //当前类:ReentrantLock.NonfairSync
//将AbstractQueuedSynchronizer的state属性设置为1
if (compareAndSetState(0, 1))
    setExclusiveOwnerThread(Thread.currentThread());
else
    acquire(1);
}

protected final boolean compareAndSetState(int expect, int update) {
    //当前类:AbstractQueuedSynchronizer
    //这个是jvm提供的工具类,调用native方法,利用cas乐观锁来替换state的属性为1
    //stateOffset是state的村偏移量,如果state的属性为0,则替换为1,替换成功返回true,否则返回false
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
  
//当前类:AbstractOwnableSynchronizer
//如果compareAndSetState方法返回true,则设置独占锁所属线程属性为当前线程
protected final void setExclusiveOwnerThread(Thread thread) {
    exclusiveOwnerThread = thread;
}

上述的代码,如果一个线程获取到锁,则lock方法结束,继续执行业务代码。

假如没有获取锁,compareAndSetState返回false,则执行acquire方法。

3.4 进入acquire方法

//当前类:AbstractQueuedSynchronizer
public final void acquire(int arg) {
    if (
        !tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
        )
           selfInterrupt();
}

//当前类:ReentrantLock.NonfairSync
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

//当前类:ReentrantLock.Sync
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    //得到state的值
    int c = getState();
    if (c == 0) { //等于0,代表没有线程持有该锁,下面的操作和3.3标题的代码一样
        if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    } // 如果state不为0,则代表有线程持有锁,判断持有锁的线程是不是当前线程,是的话state+1,重入锁,不重新获取锁,返回lock方法,继续执行业务代码
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // 返回false,代表没有抢到锁,将加入双向链表和挂起
    return false;
}

如果tryAcquire(arg)返回true,则代表重入,则!tryAcquire(arg)为false,acquire方法结束,lock方法结束。否则的话先执行addWaiter(Node.EXCLUSIVE)方法。

3.5 进入addWaiter方法

//当前类:AbstractQueuedSynchronizer
private Node addWaiter(Node mode) {
    //生成一个当前线程的Node节点,Node是AbstractQueuedSynchronizer的静态内部类
    Node node = new Node(Thread.currentThread(), mode);
    //将tail节点指向pred
    Node pred = tail;
    //如果tail不为空。则证明双向链表已经初始化了,存在head和tail节点
    if (pred != null) {
        //将当前节点的前节点设置为tail节点
        node.prev = pred;
        //假设pred为tail节点,则将当前节点设置为tail节点,cas实现
        if (compareAndSetTail(pred, node)) {
            //如果设置成功,证明当前没有其他线程竞争设置tail节点,或者当前线程抢先设置成功
            //然后把原tail节点的后节点设置为当前节点
            pred.next = node;
            //返回当前节点
            return node;
        }
    }
    //双向链表没有初始化或者当前线程没有抢先设置自己为tail节点都会到这里
    enq(node);
    return node;
}

 双向链表没有初始化或者当前线程没有抢先设置自己为tail节点,则会进入enq方法。

3.6 进入enq方法

//当前类:AbstractQueuedSynchronizer
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        // 如果双向链表没有初始化,则初始化
        if (t == null) {
            // 建一个辅助节点,设置为双向链表的head节点
            if (compareAndSetHead(new Node())) 
            //设置成功之后,将tail和head指向同一个节点,即双向链表的tail和head节点为同一个Node节点,继续循环将当前线程的节点设置为tail节点
            tail = head; 
        } else {
            // 如果tail节点不为null,将当前节点的前节点设置为tail节点,假设t现在还是tail节点,则设置当前节点为tail节点,设置失败继续循环,直到设置成功
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                //设置原tail节点的后节点为当前节点
                t.next = node;
                //返回当前节点的前节点
                return t;
            }
        }
    }
}

 addWaiter方法结束,就会返回当前节点,作为acquireQueued方法的参数。

3.7 进入acquireQueued方法

//当前类:AbstractQueuedSynchronizer
final boolean acquireQueued(final Node node, int arg) {
	boolean failed = true;
	try {
		boolean interrupted = false;
		for (;;) {
            //返回当前节点的前节点
			final Node p = node.predecessor();
            //如果前节点是head,则去获取锁,和3.4的标题的tryAcquire方法一样
			if (p == head && tryAcquire(arg)) {
                //如果抢到锁,设置当前节点为头,将该节点包含的node.thread设置为空,前节点设置为空
				setHead(node);
				p.next = null; // help GC
				failed = false;
                //返回false,当前线程的lock方法也结束,继续执行业务代码
				return interrupted;
			}
            //这个方法就是对当前节点的前节点的状态进行设置,设置为SIGNAL,代表等待唤醒
            //head节点代表当前获取锁的节点,如果它的状态为SIGNAL,就会去唤醒它的下一个节点,这个在unlock方法的源码中,可以看到
			if (shouldParkAfterFailedAcquire(p, node) &&
				parkAndCheckInterrupt())
				interrupted = true;
		}
	} finally {
		if (failed)
			cancelAcquire(node);
	}
}

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //得到前节点的状态
	int ws = pred.waitStatus;
    //如果为SIGNAL,就不用设置了,返回true,走接下来的逻辑,挂起就行了
	if (ws == Node.SIGNAL)
		return true;
    //SIGNAL的数值为-1,大于的状态只有一个int CANCELLED =  1;代表取消,所以循环去掉所有取消的状态线程,没必要去挂起了
	if (ws > 0) {
		do {
			node.prev = pred = pred.prev;
		} while (pred.waitStatus > 0);
		pred.next = node;
	} else {
        //设置当前节点的前节点状态为SIGNAL,代表等待唤醒
		compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
	}
    //返回false,继续进入循环
	return false;
}

//此时shouldParkAfterFailedAcquire方法返回true,所有的节点的前节点都为SIGNAL状态了,等待唤醒,调用park方法,挂起线程
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

此时,所有的等待线程就会形成一个双向链表,由于是非公平锁,所以会按照顺序从head节点往后去唤醒。初始化双向链表会生成下面的这种结构。


 3.8 进入unlock方法

//当前类:ReentrantLock
public void unlock() {
    sync.release(1);
}

//当前类:AbstractQueuedSynchronizer
public final boolean release(int arg) {
    //设置state的值
	if (tryRelease(arg)) {
		Node h = head;
		if (h != null && h.waitStatus != 0)
            //释放锁
			unparkSuccessor(h);
		return true;
	}
	return false;
}

//当前类:ReentrantLock
protected final boolean tryRelease(int releases) {
    //得到state的值,如果state-1等于0,则证明没有重入锁,或者重入锁已经释放完。
    //因为此时还持有锁或者没有去唤醒其他线程,所以所有的操作都是安全的。
	int c = getState() - releases;
	if (Thread.currentThread() != getExclusiveOwnerThread())
		throw new IllegalMonitorStateException();
	boolean free = false;
	if (c == 0) {
		//返回true,接下来去释放锁
        free = true;
        //设置独占锁的线程属性为null
		setExclusiveOwnerThread(null);
	}
    //设置state的值,如果state不为0,不去释放锁,代表当前unlock方法是一个重入锁调用的
	setState(c);
	return free;
}
    

上述的代码,可以只是释放一个重入锁,所以不会走unparkSuccessor方法去唤醒其他线程。

假如state为0了,进入unparkSuccessor方法唤醒其他线程。

 3.9 进入unparkSuccessor方法,释放锁

//当前类:AbstractQueuedSynchronizer
private void unparkSuccessor(Node node) {
    //传入的是head节点,得到head节点的状态
	int ws = node.waitStatus;
	if (ws < 0)
		compareAndSetWaitStatus(node, ws, 0);
    //得到head节点的下一个节点
	Node s = node.next;
    //如果下一个节点是空,或者状态不正常,为取消状态,int CANCELLED =  1;
	if (s == null || s.waitStatus > 0) {
		s = null;
        //从tail节点向前查找head节点的next节点,如果next节点状态不正常或者不存在,就会找next节点的next节点,一直找下去
		for (Node t = tail; t != null && t != node; t = t.prev)
			if (t.waitStatus <= 0)
				s = t;
	}
    //如果确实找不到需要唤醒的,就返回unlock方法结束
	if (s != null)
        //唤醒下一个等待的线程    
		LockSupport.unpark(s.thread);
}

到这里ReentrantLock的加锁和释放锁的源码就解析完了,一些不太重要的代码,没有解析,感兴趣的可以自己研究。

四、总结

ReentrantLock的源码还是很简单的,最主要的是了解双向链表的结构、了解CAS乐观锁的原理和了解LockSupport的park和unpark方法,这个类是工具类,都是调用的native方法,所以只有了解它的作用就行了。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lollala

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值