ReentrantLock源码分析

68 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

继承关系

ReentrantLock继承了Lock接口,Lock接口中定义了lock和unlock相关的操作

public class ReentrantLock implements Lock, java.io.Serializable

类的内部类

内部类结构 6-1-ReentrantLock内部类结构.png

6-2-ReentrantLock内部类结构源码截图.png

逐步分析源码

Sync

abstract static class Sync extends AbstractQueuedSynchronizer {
    // 序列号
    private static final long serialVersionUID = -5179523762034025860L;
    
    // 获取锁
    abstract void lock();
    
    // 非公平方式获取
    final boolean nonfairTryAcquire(int acquires) {
        // 当前线程
        final Thread current = Thread.currentThread();
        // 获取状态
        int c = getState();
        if (c == 0) { // 表示没有线程正在竞争该锁
            if (compareAndSetState(0, acquires)) { // 比较并设置状态成功,状态0表示锁没有被占用
                // 设置当前线程独占
                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;
    }
    
    // 试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
    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; 
    }
    
    // 判断资源是否被当前线程占有
    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
​
    // 新生一个条件
    final ConditionObject newCondition() {
        return new ConditionObject();
    }
​
    // Methods relayed from outer class
    // 返回资源的占用线程
    final Thread getOwner() {        
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
    // 返回状态
    final int getHoldCount() {            
        return isHeldExclusively() ? getState() : 0;
    }
​
    // 资源是否被占用
    final boolean isLocked() {        
        return getState() != 0;
    }
​
    /**
        * Reconstitutes the instance from a stream (that is, deserializes it).
        */
    // 自定义反序列化逻辑
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}  
​

NonfairSync

NonfairSync类继承了Sync类,表示采用非公平策略获取锁

// 非公平锁
static final class NonfairSync extends Sync {
    // 版本号
    private static final long serialVersionUID = 7316153563782823691L;
​
    // 获得锁
    final void lock() {
        if (compareAndSetState(0, 1)) // 比较并设置状态成功,状态0表示锁没有被占用
            // 把当前线程设置独占了锁
            setExclusiveOwnerThread(Thread.currentThread());
        else // 锁已经被占用,或者set失败
            // 以独占模式获取对象,忽略中断
            acquire(1); 
    }
​
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

lock方法中我们可以看到,每次调用lock,当前线程都会先去尝试是否能够改变共享变量的值,如果可以则加锁成功,如果失败,则加入CLH队列

FairSyn

FairSync类也继承了Sync类,表示采用公平策略获取锁

// 公平锁
static final class FairSync extends Sync {
    // 版本序列化
    private static final long serialVersionUID = -3000897897090466540L;
​
    final void lock() {
        // 以独占模式获取对象,忽略中断
        acquire(1);
    }
​
    /**
        * Fair version of tryAcquire.  Don't grant access unless
        * recursive call or no waiters or is first.
        */
    // 尝试公平获取锁
    protected final boolean tryAcquire(int acquires) {
        // 获取当前线程
        final Thread current = Thread.currentThread();
        // 获取状态
        int c = getState();
        if (c == 0) { // 状态为0
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) { // 不存在已经等待更久的线程并且比较并且设置状态成功
                // 设置当前线程独占
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) { // 状态不为0,即资源已经被线程占据
            // 下一个状态
            int nextc = c + acquires;
            if (nextc < 0) // 超过了int的表示范围
                throw new Error("Maximum lock count exceeded");
            // 设置状态
            setState(nextc);
            return true;
        }
        return false;
    }
}

当资源空闲时,会先判断sync队列是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。

如果资源被其他线程占用,则会添加到sync队列的尾部,而不会先去尝试进行资源的获取。

类的构造函数

ReentrantLock(),默认是采取非公平策略获取锁

public ReentrantLock() {
    // 默认非公平策略
    sync = new NonfairSync();
}
 

ReentrantLock(boolean) ,可以通过参数进行策略的切换选择

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

源码分析总结

ReentrantLock是继承与Lock接口,实现了lock接口中的一些方法,而内部类则是基于AQS模板进行类的扩展,实现方式有两种,公平锁和非公平锁,可以通过构造函数中的参数进行控制,默认是非公平锁。

自定义锁

参考ReentrantLock的实现方式,自定义一个锁

首先我们要创建一个类MyLock,并且实现Lock接口的部分方法

package com.example.demo.thread.selflock;
​
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
​
public class MyLock implements Lock{
​
    AtomicInteger state = new AtomicInteger();
​
    Thread ownerThread = null;
​
    LinkedBlockingDeque<Thread> waiters = new LinkedBlockingDeque<>();
​
    @Override
    public void lock() {
        if(!tryLock()){
            waiters.add(Thread.currentThread());
            for(;;){
                if(tryLock()){
                    waiters.poll();
                    return;
                }else{
                    LockSupport.park();
                }
            }
        }
​
    }
​
    @Override
    public void lockInterruptibly() throws InterruptedException {
​
    }
​
    @Override
    public boolean tryLock() {
        if(state.get() == 0){
            if(state.compareAndSet(0,1)){
                ownerThread = Thread.currentThread();
                return true;
            }
        }else if(ownerThread == Thread.currentThread()){
            state.set(state.get() + 1);
            return true;
        }
        return false;
    }
​
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }
​
    @Override
    public void unlock() {
        if(ownerThread != Thread.currentThread()){
            throw new RuntimeException("非法调用---当前这个锁不属于你");
        }
        if(state.decrementAndGet() == 0){
            ownerThread = null;
            Thread waiterThread = waiters.peek();
            LockSupport.unpark(waiterThread);
        }
    }
​
    @Override
    public Condition newCondition() {
        return null;
    }
}
​

基于AQS的实现原理,自定义一个简单规则,包括lock()、tryLock()、unlock()三个方法

测试用例如下

package com.example.demo.thread.selflock;
​
import java.util.Date;
​
public class LockDemo {
    static MyLock lock = new MyLock();
​
    public static void main(String[] args) throws InterruptedException {
        lock.lock(); // 其他没有获取到锁 阻塞 卡住不动
        System.out.println("主线程获取到锁 其他线程卡住不动 " + Thread.currentThread().getName() + new Date());
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "---子线程获取到锁---");
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "---子线程第2次获取到锁---");
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + "---子线程释放锁---");
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + "---子线程第2次释放锁---");
            }
        });
        thread.start();
        // 主线程睡眠3s
        Thread.sleep(3000L);
        System.out.println("3s后主线程释放锁 其他线程可以开始抢锁 " + Thread.currentThread().getName() + new Date());
        lock.unlock();
    }
}
​

结果

6-3-自定义锁测试结果截图.png