Java 并发锁-ReentrantLock

全路径名:java.util.concurrent.locks.ReentrantLock 类定义如下:

js 复制代码
/**
 * @since 1.5
 */
public class ReentrantLock implements Lock, java.io.Serializable {
    ...
}

ReentrantLock 类实现了 Lock 接口,JDK1.5 引入。

ReentrantLock 使用上分公平锁和非公平锁,两种锁机制。默认无参构造方法 ReentrantLock() 创建的是非公平锁。可以使用有参构造函数 ReentrantLock(boolean fair) 选择使用公平锁,还是非公平锁。具体实现是通过 ReentrantLock 的内部类 FairSync 和 NonfairSync 来实现的。FairSync 和 NonfairSync 是 ReentrantLock 类中抽象内部类 Sync 的子类。具体源码如下:

js 复制代码
public class ReentrantLock implements Lock, java.io.Serializable {
    ...
    private final Sync sync;
    ...
    
    abstract static class Sync extends AbstractQueuedSynchronizer {...}
    
    static final class NonfairSync extends Sync {...}
    
    static final class FairSync extends Sync {...}
    
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
 
    public void lock() {
        sync.lock();
    }
    
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    
    public void unlock() {
        sync.release(1);
    }
    
    public Condition newCondition() {
        return sync.newCondition();
    }
    ...
}

java.util.concurrent.locks.Lock 接口定义了5个方法。具体源码如下:

js 复制代码
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

通过对 ReentrantLock 类 Lock 接口 lock() 方法的实现, 来看 ReentrantLock 是如何实现公平锁的。

先说方式,再看源码更好理解一点。如果要公平,那就要有先来后到。就像超市购物结账一样:

  • 如果结账时,恰好没有人,那就直接结账。直接就拿到了锁。
  • 如果结账时,已经有人了,那就排到队伍的后面,等到你的时候才可以结账。也就是拿到了锁。

ReentrantLock 内部类 FairSync 负责实现公平锁机制,FairSync 类继承了 Sync 类,Sync类 继承了 AbstractQueuedSynchronizer 类。下面是与 lock() 方法有个的源码:

js 复制代码
static final class FairSync extends Sync {

    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;
    }
}
js 复制代码
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    ...
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    ...
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    ...
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) 
                        && parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}

FairSync 的 lock() 方法调用 AbstractQueuedSynchronizer 类 acquire() 方法获取锁。AbstractQueuedSynchronizer 类 acquire() 方法中,先使用 FairSync 类的 tryAcquire() 方法实现没有人排队的场景。

  • int c = getState(): 获取的是 AbstractQueuedSynchronizer 类的一个状态。c = 0 代表目前没有线程拿到这个锁
  • !hasQueuedPredecessors(): 没有其他线程排队
  • compareAndSetState(0, acquires):使用 CAS(Compare and Swap)CPU 硬件同步原语机制获取锁
  • setExclusiveOwnerThread(current):如果成功拿到锁,将当前线程和锁绑定
  • else if (current == getExclusiveOwnerThread()) 如果锁已经被当前线程绑定,状态 c 加 1。这块就体现了 ReentrantLock 可重入的概念。同一个线程未释放锁的情况下可以重复拿到锁,每次状态 c 加1。

AbstractQueuedSynchronizer 类 acquire() 方法中,如果没人排队的情况下,未能成功抢到锁。那就进入了排队的场景:

  • for (;;) :无限循环
  • if (p == head && tryAcquire(arg)):只有排队首的才有资格竞争锁。p 是获得锁的线程
  • parkAndCheckInterrupt() 这个方法里实现的未获得锁的线程阻塞。这里不讨论。

看完 ReentrantLock 公平锁的实现,非公平锁的实现就简单了。非公平锁是通过 ReentrantLock 内部类 实现的,源码如下:

js 复制代码
static final class NonfairSync extends Sync {
    
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

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

abstract static class Sync extends AbstractQueuedSynchronizer {
    ...
    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;
    }
    ...
}

从源码看一看出,不排队,直接调用 compareAndSetState(0, 1) 方法抢。这就是非公平锁。

相关推荐
FelixBitSoul2 小时前
Go 语言面试深度全攻略:从工程化到底层原理,一文通杀
后端·go
用户537712853042 小时前
如何通过自定义注解实现零代码侵入的方法日志记录
后端
橙子不要熬夜2 小时前
构建一个会调用工具和定时任务的AI智能助手
后端
Gopher_HBo2 小时前
图解Go语言逃逸
后端
MiNG MENS2 小时前
Spring Boot + Vue 全栈开发实战指南
vue.js·spring boot·后端
2601_949816582 小时前
Spring Boot--@PathVariable、@RequestParam、@RequestBody
java·spring boot·后端
SimonKing2 小时前
轻量级富文本编辑器Quill,保姆级教程,5分钟快速上手
java·后端·程序员
做个文艺程序员2 小时前
生产级 AI 服务:限流、鉴权与可观测性【OpenClAW + Spring Boot 系列 第6篇·终章】
人工智能·spring boot·后端
Ares-Wang2 小时前
flask》》信号
后端·python·flask