ReentrantLock的概念和结构

概念

  • ReentrantLock基于AQS,在并发编程中可以实现公平锁和非公平锁来对共享资源进行同步,同时,和synchronized一样,ReentrantLock支持可重入,除此之外,ReetrantLock在调度上更加灵活,支持更多丰富的功能。

结构

  • ReetrantLock实现Lock接口,同时里面包含三个内部类,Sync、FairSync、NonfairSync
  • Sync继承AQS,除了lock()、readObject()方法其他的都用final修饰,防止子类修改,是对AQS的封装和拓展,本身实现已完全可靠、不希望被外部破坏;
  • FairSync、NonfairSync继承Sync,都只重写Sync的lock()、AQS的tryAcquire()方法


公平锁与非公平锁

构造函数

  • 默认采用非公平的方式;
  • true是公平、false是非公平
public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

说明

  • FairSync:只要资源被其他线程占用,该线程就会添加到sync queue中的尾部,而不是先尝试获取资源;
  • NonFairSync:每一次都尝试获取资源,如果此时资源恰好被释放,则会被当前线程获取,造成不公平现象,当获取不成功,再加入队列尾部。


Lock类的常见方法

lock.lock()

  • 用于获取锁,如果当前锁被其他线程占用,将会等待直到获取为止。
流程
  1. 根据传入具体的实体类,判断调用FairSync.lock() 或者 NonfairSync.lock();
  2. 通过CAS尝试获取锁,失败调用AQS的acquire();
  3. AQS的acquire()会首先调用tryAcquire()、对应有不同的实体类重写FairSync.tryAcquire() 或者 NonfairSync.tryAcquire();
  4. FairSync.tryAcquire() 会直接返回Sync的nonfairTryAcquire(),里面获取state,如果为0,进行CAS尝试锁,不为0,如果当前线程就是占有锁的线程,进行重入,使用nextc记录重入次数
  5. 如果tryAcquire()失败,调用 addWaiter() 将当前线程封装成Node,加入队列中;acquireQueued() 将队列的head之后的一个节点在自旋CAS获取锁,其他线程都被挂起。挂起的线程等待执行release()释放锁资源

以NonfairSync为例的详解
  • sync通过构造函数传入初始化,判断是new FairSync 还是 new NonfairSync
public void lock() {
        sync.lock();
    }

NonfairSync的lock方法

  • 首先尝试一次对锁的获取,如果CAS成功,那么当前线程成功获得锁,同时将当前线程置为独占线程,并返回true;
  • 否则,调用AQS的acquire方法;
final void lock() {
            //  state为0,表示锁状态为空闲
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

AQS的acquire方法

  • 首先会调用tryAcquire()、子类NonfairSync重写了此方法;
  • 如果失败调用addWaiter(),将调用此方法的线程封装成为一个结点并放入Sync queue;
  • 调用acquireQueued(),将在sync queue中的结点不断尝试获取资源,如果成功返回true,失败返回false。
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

NonfairSync.tryAcquire() 直接返回 Sync的nonfairTryAcquire()

  • 获取state,该值由AQS维护;
  • 当state为0,表示锁状态空闲,便可以通过一次CAS来原子地更改state,如果state更改成功,则代表获取了锁,将当前线程置为独占线程,并返回true;
  • 当state不为0,说明锁被占用,判断当前线程是否为独占线程(占用锁的线程);如果是,则代表重入,使用nextc记录重入次数;

补充:

判断state是否小于0的意义?

  • state使用int类型存储,最大的有符号数为2147483647,一旦超出便会溢出变为负数;也可以理解为,ReetrantLock允许重入的最大次数其实就是2147483647

可重入性?

  • 一个线程可以不用释放而重复获取一个锁n次,只是在释放的时候也需要相应的释放n次。
// NonfairSync
protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

// Sync
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
补充:fairSync.lock()、tryAcquire()的区别
  • 判断锁是否空闲,如果空闲,并不是直接尝试通过CAS获取锁,而是需要判断是否存在前置等待结点。如果不存在,说明队列中确实已经轮到当前线程尝试获取锁,体现了公平性。
        final void lock() {
                    acquire(1);
                }



        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                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;
        }

lock.tryLock()

  • 尝试获取锁、tryLock()的操作都是非公平的,不管传入的是公平还是非公平
  • nonfairTryAcquire()具体详解上面讲lock()的时候已经涉及到了。
// ReentrantLock
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

// Sync
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

lock.unlock()

  • 释放锁,tryRelease()需要完全释放;如果遇到重入的情况,可能会返回释放多次。
// ReentrantLock
public void unlock() {
        sync.release(1);
    }

// AQS
 public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

// Syn
protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

lock.lockInterruptibly()

用于获取锁,如果当前线程在等待锁的过程中被中断,会退出等待并抛出异常。

lock.newCondition()

创建condition对象,相当于Object类的wait、notify方法


参考