Skip to content

ReentrantLock

ReentrantLock可以创建公平锁或非公平锁、响应中断、超时等待、按条件唤醒等。

在某些场景下,使用ReentrantLock更适合,功能更强大。

源码解析

类结构

java
// 实现Lock接口
public class ReentrantLock implements Lock {

    // 只有一个Sync同步变量
    private final Sync sync;

    // Sync继承自AQS,主要逻辑都在这里面
    abstract static class Sync extends AbstractQueuedSynchronizer {
    }

    // Sync的两个子类,分别实现了公平锁和非公平锁
    static final class FairSync extends Sync {
    }
    static final class NonfairSync extends Sync {
    }
}
// 实现Lock接口
public class ReentrantLock implements Lock {

    // 只有一个Sync同步变量
    private final Sync sync;

    // Sync继承自AQS,主要逻辑都在这里面
    abstract static class Sync extends AbstractQueuedSynchronizer {
    }

    // Sync的两个子类,分别实现了公平锁和非公平锁
    static final class FairSync extends Sync {
    }
    static final class NonfairSync extends Sync {
    }
}

ReentrantLock的类结构非常简单,并没有直接继承AQS抽象类,而是实现了Lock接口。

采用组合的方式使用Sync同步类实现锁的功能,而Sync同步类才是真正继承AQS抽象类。

Sync抽象类下面有两个子类,分别实现公平锁和非公平锁。

java
public interface Lock {

    // 加锁
    void lock();

    // 加可中断的锁
    void lockInterruptibly() throws InterruptedException;

    // 尝试加锁
    boolean tryLock();

    // 一段时间内,尝试加锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    // 释放锁
    void unlock();

    // 新建条件状态
    Condition newCondition();
}
public interface Lock {

    // 加锁
    void lock();

    // 加可中断的锁
    void lockInterruptibly() throws InterruptedException;

    // 尝试加锁
    boolean tryLock();

    // 一段时间内,尝试加锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    // 释放锁
    void unlock();

    // 新建条件状态
    Condition newCondition();
}

AQS定义了一些有关具体加锁、释放锁的抽象方法,留给子类去实现。

由于ReentrantLock使用的是独占锁,所以只需要实现独占锁相关的方法

ReentrantLock构造方法

创建ReentrantLock对象,可以指定使用公平锁还是非公平锁,默认使用非公平锁,非公平锁的性能更好。

java
// 默认的构造方法,使用非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}

// 传true,可以指定使用公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
// 默认的构造方法,使用非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}

// 传true,可以指定使用公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

NonfairSync源码

从父类ReentrantLock的加锁方法入口看起:

java
public class ReentrantLock implements Lock {
    // 加锁入口方法
    public void lock() {
        // 调用Sync中加锁方法
        sync.lock();
    }
}
public class ReentrantLock implements Lock {
    // 加锁入口方法
    public void lock() {
        // 调用Sync中加锁方法
        sync.lock();
    }
}

子类非公平锁NonfairSync的加锁方法:

java
// 非公平锁
static final class NonfairSync extends Sync {

    // 加锁
    final void lock() {
        // 1. 先尝试加锁(使用CAS设置state=1)
        if (compareAndSetState(0, 1)) {
            // 2. 如果加锁成功,就把当前线程设置为持有锁线程
            setExclusiveOwnerThread(Thread.currentThread());
        } else {
            // 3. 如果加锁失败,再调用父类AQS中实际的加锁逻辑
            acquire(1);
        }
    }
}
// 非公平锁
static final class NonfairSync extends Sync {

    // 加锁
    final void lock() {
        // 1. 先尝试加锁(使用CAS设置state=1)
        if (compareAndSetState(0, 1)) {
            // 2. 如果加锁成功,就把当前线程设置为持有锁线程
            setExclusiveOwnerThread(Thread.currentThread());
        } else {
            // 3. 如果加锁失败,再调用父类AQS中实际的加锁逻辑
            acquire(1);
        }
    }
}

先尝试使用CAS加锁(也就是把state从0设置成1),加锁成功,就把当前线程设置为持有锁线程。

在锁竞争不激烈的情况下,很大概率可以加锁成功,也就不用走else中复杂的加锁逻辑了。

如果没有加锁成功,还是需要走else中调用父类AQS的acquire()方法,而acquire()方法又需要调用子类的tryAcquire()方法。

调用链路如下:

image-20240622214625882

实际的加锁逻辑在Sync.nonfairTryAcquire()方法里面。

java
abstract static class Sync extends AbstractQueuedSynchronizer {
    // 非公平锁的最终加锁方法
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 1. 获取同步状态
        int c = getState();
        // 2. state=0表示无锁,先尝试加锁(使用CAS设置state=1)
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                // 3. 加锁成功,就把当前线程设置为持有锁线程
                setExclusiveOwnerThread(current);
                return true;
            }
            // 4. 如果当前线程已经持有锁,执行可重入的逻辑
        } else if (current == getExclusiveOwnerThread()) {
            // 5. 加锁次数+acquires
            int nextc = c + acquires;
            // 6. 超过tnt类型最大值,溢出了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // 7. 加锁失败,返回false
        return false;
    }
}
abstract static class Sync extends AbstractQueuedSynchronizer {
    // 非公平锁的最终加锁方法
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 1. 获取同步状态
        int c = getState();
        // 2. state=0表示无锁,先尝试加锁(使用CAS设置state=1)
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                // 3. 加锁成功,就把当前线程设置为持有锁线程
                setExclusiveOwnerThread(current);
                return true;
            }
            // 4. 如果当前线程已经持有锁,执行可重入的逻辑
        } else if (current == getExclusiveOwnerThread()) {
            // 5. 加锁次数+acquires
            int nextc = c + acquires;
            // 6. 超过tnt类型最大值,溢出了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // 7. 加锁失败,返回false
        return false;
    }
}

总结-非公平锁的加锁逻辑也很简单,大致分为三步:

  1. 判断同步状态state,如果state=0表示无锁,就尝试加锁,如果加锁成功,就更新state和线程owner。
  2. 如果线程owner就是当前线程,执行可重入锁的逻辑,更新state值。
  3. 否则加锁失败,返回false。

再看释放锁的调用流程,公平锁和非公平锁流程是一样的,最终都是执行Sync.tryRelease()方法:

本质是 更新同步状态 state 和 线程 owner 。

java
abstract static class Sync extends AbstractQueuedSynchronizer {
    // 释放锁
    protected final boolean tryRelease(int releases) {
        // 1. 同步状态减去释放锁次数
        int c = getState() - releases;
        // 2. 校验当前线程不持有锁,就报错
        if (Thread.currentThread() != getExclusiveOwnerThread()) {
            throw new IllegalMonitorStateException();
        }
        boolean free = false;
        // 3. 判断同步状态是否等于0,无锁后,就删除持有锁的线程
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}
abstract static class Sync extends AbstractQueuedSynchronizer {
    // 释放锁
    protected final boolean tryRelease(int releases) {
        // 1. 同步状态减去释放锁次数
        int c = getState() - releases;
        // 2. 校验当前线程不持有锁,就报错
        if (Thread.currentThread() != getExclusiveOwnerThread()) {
            throw new IllegalMonitorStateException();
        }
        boolean free = false;
        // 3. 判断同步状态是否等于0,无锁后,就删除持有锁的线程
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}

FairSync源码

最终的加锁方法是FairSync.tryAcquire(),具体源码:

java
static final class FairSync extends Sync {

    // 实现父类的加锁逻辑
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 1. 获取同步状态
        int c = getState();
        // 2. state=0表示无锁,先尝试加锁(使用CAS设置state=1)
        if (c == 0) {
            // 3. 判断当前线程是不是头节点的下一个节点(讲究先来后到)
            if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
            // 4. 如果当前线程已经持有锁,执行可重入的逻辑
        } else if (current == getExclusiveOwnerThread()) {
            // 5. 加锁次数+acquires
            int nextc = c + acquires;
            // 6. 超过tnt类型最大值,溢出了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

    // 判断当前线程是不是头节点的下一个节点(讲究先来后到)(AQS)
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
    }
}
static final class FairSync extends Sync {

    // 实现父类的加锁逻辑
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 1. 获取同步状态
        int c = getState();
        // 2. state=0表示无锁,先尝试加锁(使用CAS设置state=1)
        if (c == 0) {
            // 3. 判断当前线程是不是头节点的下一个节点(讲究先来后到)
            if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
            // 4. 如果当前线程已经持有锁,执行可重入的逻辑
        } else if (current == getExclusiveOwnerThread()) {
            // 5. 加锁次数+acquires
            int nextc = c + acquires;
            // 6. 超过tnt类型最大值,溢出了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

    // 判断当前线程是不是头节点的下一个节点(讲究先来后到)(AQS)
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
    }
}

公平锁的加锁逻辑跟非公平锁大致相同,唯一的区别是在第三步,更新同步状态state之前,增加了一个判断条件hasQueuedPredecessors()方法,用于判断当前线程是不是头节点的下一个节点。

因为等待的线程都要在同步队列中排队,同步队列的头节点是空节点,头节点的下一个节点相当于第一个有效节点,这个节点优先获取锁,在这里体现了公平锁的逻辑。

公平锁的释放锁逻辑跟非公平锁一样,上面已经讲过。

示例

java
public class ReentrantLockDemo {
    
    public static void main(String[] args) {
        // 1. 创建ReentrantLock对象
        ReentrantLock lock = new ReentrantLock();
        // 2. 加锁
        lock.lock();
        try {
            // 3. 这里执行具体的业务逻辑
        } finally {
            // 4. 释放锁
            lock.unlock();
        }
    }
}
public class ReentrantLockDemo {
    
    public static void main(String[] args) {
        // 1. 创建ReentrantLock对象
        ReentrantLock lock = new ReentrantLock();
        // 2. 加锁
        lock.lock();
        try {
            // 3. 这里执行具体的业务逻辑
        } finally {
            // 4. 释放锁
            lock.unlock();
        }
    }
}

ReentrantLock的使用非常简单,调用lock()方法加锁,unlock()方法释放锁;需要配合try/finally关键字使用,保证在代码执行出错的时候也能释放锁。

手写锁

要求:同一时间只能有一个线程持有锁,不要求可重入(无视反复加锁)

java
public class Main {
    public static void main(String[] args) throws InterruptedException {
        
    }

    /**
     * 自行实现一个最普通的独占锁
     * 要求:同一时间只能有一个线程持有锁,不要求可重入
     */
    private static class MyLock implements Lock {

        /**
         * 设计思路:
         * 1. 锁被占用,那么exclusiveOwnerThread应该被记录,并且state = 1
         * 2. 锁没有被占用,那么exclusiveOwnerThread为null,并且state = 0
         */
        private static class Sync extends AbstractQueuedSynchronizer {
            @Override
            protected boolean tryAcquire(int arg) {
                // 无需可重入功能,如果是当前线程直接返回true
                if(isHeldExclusively()) return true;     
                // CAS操作进行状态替换
                if(compareAndSetState(0, arg)){    
                    // 成功后设置当前的所有者线程
                    setExclusiveOwnerThread(Thread.currentThread());    
                    return true;
                }
                return false;
            }

            @Override
            protected boolean tryRelease(int arg) {
                if(getState() == 0)
                    // 没加锁情况下是不能直接解锁的
                    throw new IllegalMonitorStateException();   
                // 只有持有锁的线程才能解锁
                if(isHeldExclusively()){     
                    // 设置所有者线程为null
                    setExclusiveOwnerThread(null);    
                    // 状态变为0
                    setState(0);    
                    return true;
                }
                return false;
            }

            @Override
            protected boolean isHeldExclusively() {
                return getExclusiveOwnerThread() == Thread.currentThread();
            }

            protected Condition newCondition(){
                // 直接用现成的
                return new ConditionObject();    
            }
        }

        private final Sync sync = new Sync();

        @Override
        public void lock() {
            sync.acquire(1);
        }

        @Override
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }

        @Override
        public boolean tryLock() {
            return sync.tryAcquire(1);
        }

        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(time));
        }

        @Override
        public void unlock() {
            sync.release(1);
        }

        @Override
        public Condition newCondition() {
            return sync.newCondition();
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        
    }

    /**
     * 自行实现一个最普通的独占锁
     * 要求:同一时间只能有一个线程持有锁,不要求可重入
     */
    private static class MyLock implements Lock {

        /**
         * 设计思路:
         * 1. 锁被占用,那么exclusiveOwnerThread应该被记录,并且state = 1
         * 2. 锁没有被占用,那么exclusiveOwnerThread为null,并且state = 0
         */
        private static class Sync extends AbstractQueuedSynchronizer {
            @Override
            protected boolean tryAcquire(int arg) {
                // 无需可重入功能,如果是当前线程直接返回true
                if(isHeldExclusively()) return true;     
                // CAS操作进行状态替换
                if(compareAndSetState(0, arg)){    
                    // 成功后设置当前的所有者线程
                    setExclusiveOwnerThread(Thread.currentThread());    
                    return true;
                }
                return false;
            }

            @Override
            protected boolean tryRelease(int arg) {
                if(getState() == 0)
                    // 没加锁情况下是不能直接解锁的
                    throw new IllegalMonitorStateException();   
                // 只有持有锁的线程才能解锁
                if(isHeldExclusively()){     
                    // 设置所有者线程为null
                    setExclusiveOwnerThread(null);    
                    // 状态变为0
                    setState(0);    
                    return true;
                }
                return false;
            }

            @Override
            protected boolean isHeldExclusively() {
                return getExclusiveOwnerThread() == Thread.currentThread();
            }

            protected Condition newCondition(){
                // 直接用现成的
                return new ConditionObject();    
            }
        }

        private final Sync sync = new Sync();

        @Override
        public void lock() {
            sync.acquire(1);
        }

        @Override
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }

        @Override
        public boolean tryLock() {
            return sync.tryAcquire(1);
        }

        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(time));
        }

        @Override
        public void unlock() {
            sync.release(1);
        }

        @Override
        public Condition newCondition() {
            return sync.newCondition();
        }
    }
}