Fork me on GitHub

ReentrantLock原理分析

概述

  ReentrantLock是基于AQS独占模式实现的一种可重入锁,与synchronized不同的是,ReentrantLock提供了公平锁和非公平锁的选择。其本质是基于操作系统的管程实现的。本文就分析一下ReentrantLock的实现原理,由于AQS在AQS-独占模式分析已经介绍过,所以涉及到AQS的地方不会过多介绍,本文会涉及到源码,文章会比较臃肿。

ReentrantLock整体结构

                

 

 

 从上图可以看出:

  • ReentrantLock实现了Lock接口
  • ReentrantLock内部是基于Sync这个抽象类实现的,而Sync继承了AQS,重写了部分AQS的方法
  • Sync有两个继承类,FairSync是实现公平锁的,而NonFairSync是实现非公平锁的

下面就分析一下每一块的代码

Sync抽象类

 1 //继承AQS
 2 abstract static class Sync extends AbstractQueuedSynchronizer {
 3         private static final long serialVersionUID = -5179523762034025860L;
 4      //子类实现加锁方法,因为有公平锁和非公平锁,所以这里没有实现
 5         abstract void lock();
 6      //该方法时tryLock方法调用的,tryLock方法是尝试获取锁,如果没有获取成功,就直接返回
 7         //不会阻塞,非公平的方式,直接竞争锁
 8         final boolean nonfairTryAcquire(int acquires) {
 9             final Thread current = Thread.currentThread();
10             int c = getState();
11             //该state是AQS中的,如果为0表示没有别的线程占有锁
12             if (c == 0) {
13                 //通过CAS加锁
14                 if (compareAndSetState(0, acquires)) {
15                     //加锁成功,将持有锁的线程改成当前线程
16                     setExclusiveOwnerThread(current);
17                     return true;
18                 }
19             }
20             //如果持有锁的线程就是当前线程,就是可重入锁,直接修改状态
21             else if (current == getExclusiveOwnerThread()) {
22                 int nextc = c + acquires;
23                 if (nextc < 0) // overflow
24                     throw new Error("Maximum lock count exceeded");
25                 setState(nextc);
26                 return true;
27             }
28             //否则加锁失败,直接返回
29             return false;
30         }
31      //释放锁
32         protected final boolean tryRelease(int releases) {
33             int c = getState() - releases;
34             if (Thread.currentThread() != getExclusiveOwnerThread())
35                 throw new IllegalMonitorStateException();
36             boolean free = false;
37             if (c == 0) {
38                 free = true;
39                 setExclusiveOwnerThread(null);
40             }
41             setState(c);
42             return free;
43         }
44         //判断当前线程是不是持有锁的线程
45         protected final boolean isHeldExclusively() {
46             // While we must in general read state before owner,
47             // we don't need to do so to check if current thread is owner
48             return getExclusiveOwnerThread() == Thread.currentThread();
49         }
50         //在AQS中的创建ConditionObject对象,保存等待线程
51         final ConditionObject newCondition() {
52             return new ConditionObject();
53         }
54 
55         final Thread getOwner() {
56             return getState() == 0 ? null : getExclusiveOwnerThread();
57         }
58 
59         final int getHoldCount() {
60             return isHeldExclusively() ? getState() : 0;
61         }
62  
63         final boolean isLocked() {
64             return getState() != 0;
65         }
66 
67         private void readObject(java.io.ObjectInputStream s)
68             throws java.io.IOException, ClassNotFoundException {
69             s.defaultReadObject();
70             setState(0); // reset to unlocked state
71         }
72     }

Sync抽象类总结

  • 该类主要实现了一个非公平的方式尝试获取锁,如果成功,ok,如果失败,直接返回,不会阻塞或者空转等待
  • 实现了一个释放锁的方法,该方法是公平锁和非公平锁公用的方法
  • 剩下的都是一些判断的方法,很简单 

NonFairSync类分析

 1  static final class NonfairSync extends Sync {
 2         private static final long serialVersionUID = 7316153563782823691L;
 3         //实现加锁方法,ReentrantLock默认就是这个方法,是一个非公平锁
 4         final void lock() {
 5             if (compareAndSetState(0, 1))
 6                 setExclusiveOwnerThread(Thread.currentThread());
 7             else
 8                 acquire(1);
 9         }
10         //这里就是调用上面Sync类中尝试获取锁的方法
11         protected final boolean tryAcquire(int acquires) {
12             return nonfairTryAcquire(acquires);
13         }
14     }

FairSync类分析

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        //公平锁获取锁的实现和上面就不一样,这里没有通过CAS尝试获取锁,而是直接调用acquire方法
        final void lock() {
            acquire(1);
        }
        //尝试获取锁
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //这里就是和非公平锁不同的地方,这里调用了hasQueuedPredecessors,后面会分析这个方法
                //剩下的和公平锁的实现就是一样的
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

 

进入AbstractQueuedSynchronizer #hasQueuedPredecessors()方法

 1   public final boolean hasQueuedPredecessors() {
 2          Node t = tail; // Read fields in reverse initialization order
 3          Node h = head;
 4          Node s;
 5           //当前队列中不只有一个节点
 6           //判断第二个节点是否为空,这个主要是为了防止第三个条件发生空指针异常,因为第一个条件虽然判断了队列中有第二个节点
 7           //但是由于是在多线程环境下,随时都有可能第一个节点被干掉,第二个节点变成第一个节点,然后第二个就是null了,再之后就会发生空
 8           //指针异常,这个返回的主要是为了判断当前线程是不是队列中的第二个节点,因为队列的第一个节点是虚拟节点,第二个节点才有获取锁的资格
 9           //在公平锁的情况下是这样,但是非公平锁就不是这样了
10          return h != t &&
11              ((s = h.next) == null || s.thread != Thread.currentThread());
12      }

总结:公平锁的实现就是严格按照入队的先后顺序获取锁。

Lock接口

public interface Lock {

    void lock();
    //获取可中断锁
    void lockInterruptibly() throws InterruptedException;
    //尝试获取锁
    boolean tryLock();
    //try获取锁,设置锁最大等待时间,如果超时就取消获取
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
   
    void unlock();

    Condition newCondition();
}

ReentrantLock构造方法

public ReentrantLock() {
        sync = new NonfairSync();
    }
//根据传入参数不同,初始化不同的类
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

ReentrantLock和Synchronized对比

  • 与 synchronized 相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候
  • ReentrantLock 还提供了条件 Condition ,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合。
  • ReentrantLock 支持中断处理,且性能较 synchronized 会好些。
  • ReentrantLock实现了公平锁和非公平锁的机制,而synchronized只有非公平一种模式
  • synchronized不需要手动释放锁,但是ReentrantLock释放锁的时候,需要程序员自己释放
  • ReentrantLock和synchronized都是通过内存屏障来保证有序性、可见性的,只不过ReentrantLock是volatile实现的,而synchronized不是使用volatile,但是volatile和synchronized在保证有序性和可见性上使用的内存屏障是一样的,所以也就是说两者基本相同

总结

本文介绍ReentrantLock源码结构以及实现原理,涉及到AQS的部分没有详细说明,主要是另一个篇文章已经介绍过,最后介绍了ReentrantLock和sychronized的区别。

 

参考:

【死磕 Java 并发】—– J.U.C 之重入锁:ReentrantLock

  

posted @ 2020-09-04 18:16  猿起缘灭  阅读(302)  评论(0编辑  收藏  举报