java 线程——ReentrantLock 源码解析

ReentrantLock 源码解析

属性

构造方法

可重入非公平锁

获取锁

步骤总结

可重入公平锁

加锁

总结步骤

锁的释放

小结


属性

构造方法

ReentrantLock  类只有两个构造方法,默认的构造方法是非公平锁。

ReentrantLock(boolean fair)   当 fair 为true 时是公平锁,当fair 为false 时是公平锁。

从构造方法可以看出ReentrantLock 类的关键还是这个syns 对象,我们重点看一下这个类以及它的两个子类FairSync 和 NonfairSync。这两个子类分别实现了公平锁和非公平锁。

abstract static class Sync extends AbstractQueuedSynchronizer 

上面这个就是Sync 类,它继承了AbstractQueuedSynchronizer(AQS) 类。

我们来看看他是如何实现可重入性和不可重入性的

可重入非公平锁

获取锁

		//加锁
        final void lock() {
        	//CAS设置state状态,若原值是0,将其置为1
            if (compareAndSetState(0, 1))
				//将当前线程标记为已持有锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
				//若设置失败,调用AQS的acquire方法
                acquire(1);
        }

若线程拿到了锁就设置当前锁的拥有这为自己,若没有拿到锁 ,就调用acquire()方法,这个方法又调用了非公平锁重写的tryAcquire()方法

 protected final boolean tryAcquire(int acquires) {
      return nonfairTryAcquire(acquires);
 }
final boolean nonfairTryAcquire(int acquires) {

			final Thread current = Thread.currentThread();//当前线程
			int c = getState();//当前状态值
            if (c == 0) {
				//若state为0,意味着没有线程获取到资源,CAS将state设置为1
				//并将当前线程标记我获取到排他锁的线程,返回true
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
			//当前线程拿到了锁
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;//state累加1
                if (nextc < 0) 
                    throw new Error("Maximum lock count exceeded");
				//设置state,此时state大于1,代表着一个线程多次获锁,state的值即是线程重入的次数
				setState(nextc);
                return true;
            }
            return false;
        }

步骤总结

  • 若当前锁没有被拿走,CAS操作修改锁的状态,并设置当前线程拥有锁。
  • 若当前线程被自身拥有,将锁的线程加1
  • 否则,获取锁失败

可重入公平锁

加锁

//获取锁
        final void lock() {
            acquire(1);
        }

获取锁调用的是AQS的 acquire() 方法。

public final void acquire(int arg) {
		//尝试获取锁,获取失败时加入等待队列。
		if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

acquire() 方法首先调用tryAcquire(arg) 方法,如果获取失败,就加入等待队列,加入成功就自己中断。

   protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();//当前线程
            int c = getState();//当前状态
            if (c == 0) {//没有线程获得锁
            //判断在时间顺序上有没有线程在等待,如果没有线程等待则,CAS设置state,
            //并标记当前线程为持有排他锁的线程;反之,不能获取!这即是公平的处理方式。
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
			//当前线程获得了锁
            else if (current == getExclusiveOwnerThread()) {
				//state累加
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
				//设置state
				setState(nextc);
				//获取成功
                return true;
            }
			//获取失败
            return false;
        }
    }

总结步骤

  • 判断当前锁有没有被其它线程拿走
  • 若锁现在空闲就判断它前面有没有线程在等待若有线程在等待,就加入等待队列。
  • 若锁被当前线程拥有使锁的状态加1

锁的释放

1)首先尝试释放锁,如果要求释放数等于锁状态数,那么将锁状态位清0,清除锁所有者,返回true;否则返回false;

(2)如果(1)返回的是true,说明锁完全释放。接下来将检查等待队列,并选择一个waitStatus处于等待状态的节点下的线程unpark(恢复),选择的依据是从尾节点开始,选取最靠近头节点的等待节点,同时清理队列中线程被取消的节点;

(3)如果(1)返回false,说明锁只是部分释放,当前线程仍旧持有该锁;

public final boolean release(int arg) {
 8        if (tryRelease(arg)) {   
 9            Node h = head;
10            if (h != null && h.waitStatus != 0)
11                unparkSuccessor(h);
12            return true;
13        }
14        return false;
15    }
16
17
18 protected final boolean tryRelease(int releases) {
19            int c = getState() - releases;   //重入锁加锁的次数-释放数量
20            if (Thread.currentThread() != getExclusiveOwnerThread())   //判断独占锁是否为当前线程所有
21                throw new IllegalMonitorStateException();
22            boolean free = false;
23            if (c == 0) {       //加锁次数=释放数量
24                free = true;
25                setExclusiveOwnerThread(null);     //清除锁拥有者标识
26            }
27            setState(c);        //设置加锁状态
28            return free;
29        }
30

小结

通过上面的分析,我们大概了解了 ReentrantLock 的流程,ReentrantLock 内部有三个重要的静态内部类,Sync 、fairSync、NonfairSync。其中fairSync、NonfairSync继承自Sync 类,调用Sync的公用逻辑,然后再在各自内部完成自己特定的逻辑(公平或非公平)。Sync作为ReentrantLock中公用的同步组件,继承了AQS,要利用AQS复杂的顶层逻辑,线程排队,阻塞,唤醒等等。

猜你喜欢

转载自blog.csdn.net/Alyson_jm/article/details/82845414