JDK源码解析之ReentrantLock

前言:

    之前说过,JDK有两种锁的方式,一种是Synchronized,一种就是CAS

    基于CAS实现的Unsafe我们无法直接使用,我们一般就是使用ReentrantLock来实现锁的功能

    那么本文我们就来看下ReentrantLock是如何实现CAS锁的

    建议读者也看一下关于AbstractQueuedSynchronizer的源码解析,AQS作为一个基础类,ReentrantLock会用到里面的很多方法

    可以参考下笔者的另一篇博客:JDK源码解析之AbstractQueuedSynchronizer

1.ReentrantLock结构分析

public class ReentrantLock implements Lock, java.io.Serializable {
    // 只有这么一个成员变量
    private final Sync sync;
    
    // 两种构造方法
    public ReentrantLock() {
        // 默认是非公平锁
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        // 可以让用户自主选择公平锁或非公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }

    1)有关于ReentrantLock.Sync

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    abstract void lock();

    ...
}

    2)ReentrantLock.FairSync公平锁

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        ...
    }
}

    3)ReentrantLock.NonFairSync非公平锁

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

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

    总结:通过上面的结构分析可知

    基础的实现类就是Sync,而Sync继承了AQS

    非公平锁NonFairSync继承了Sync

    公平锁FairSync也继承了Sync

    4)ReentrantLock的基本使用

    就是我们之前看过的ArrayBlockingQueue.offer(E e)

// ArrayBlockingQueue.offer(E e)
public boolean offer(E e) {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    // 获取锁
    lock.lock();
    try {
        if (count == items.length)
            return false;
        else {
            enqueue(e);
            return true;
        }
    } finally {
        // 释放锁
        lock.unlock();
    }
}

    由上例可知:ReentrantLock的使用还是比较简单的,lock()方法就是用来获取锁;unlock()就是用来释放锁。

    下面来着重分析一下这两个方法(我们来分析默认的非公平锁

2.ReentrantLock.lock()获取锁(非公平锁NonFairSync

// ReentrantLock.lock()
public void lock() {
    sync.lock();
}

// NonFairSync.lock()
final void lock() {
    // 使用cas设置state为1
    if (compareAndSetState(0, 1))
        // 如果设置成功则置当前线程为独占线程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 如果设置state失败,说明已经被其他线程设置成功了,
        // 则进入AQS.acquire方法
        // 关于acquire的具体细节可以参考笔者关于AQS分析的博客
        acquire(1);
}

// AbstractQueuedSynchronizer.compareAndSetState()
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

    总结:NonFairSync.lock()方法还是比较简单的,直接就是使用CAS来设置AQS.state=1,成功则说明当前线程已经竞争到锁,可以继续下面的业务处理;如果失败,则说明当前线程竞争锁失败,需要进入队列等待锁被释放继续后面的竞争。

3.ReentrantLock.unlock()释放锁(非公平锁NonFairSync

// ReentrantLock.unlock()
public void unlock() {
    sync.release(1);
}

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

    总结:ReentrantLock.unlock()直接就调用了AQS.release()

    具体不再分析,读者可参考JDK源码解析之AbstractQueuedSynchronizer​​​​​​​

4.ReentrantLock的其他加锁方式  

    1)ReentrantLock.tryLock()尝试锁,如果失败则直接返回,不再等待

// ReentrantLock.tryLock()
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

// Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
    // 1.获取当前线程及状态
    final Thread current = Thread.currentThread();
    int c = getState();
    // state=0则意味着没有线程获取到锁,使用CAS将state设置为1,
    // 返回true代表当前线程获取到锁
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    
    // 2.如果发现c!=0但是持有锁的线程是当前线程的话,
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        // state累加,如果<0了,说明已经溢出了,这个时候就只能报错
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        
        // 大于0的情况是怎么回事呢?
        // 当前线程多次获取这个锁,则每获取一次就加1
        setState(nextc);
        return true;
    }
    
    // 如果c!=0而且持有锁的线程也不是当前线程,则直接返回false
    return false;
}

    2)ReentrantLock.tryLock(long time,TimeUnit unit)尝试加锁,如果在规定时间内还是失败则直接返回,不再等待

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

// AbstractQueuedSynchronizer.tryAcquireNanos()
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
    throws InterruptedException {
    // 1.如果线程已经被设置中断状态,则直接抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    
    // 2.tryAcquire调用的是Sync.tryAcquire()
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

// Sync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
    // 直接调用了nonfairTryAcquire()方法,在1)中我们已经分析了
    return nonfairTryAcquire(acquires);
}

// AbstractQueuedSynchronizer.doAcquireNanos()
private boolean doAcquireNanos(int arg, long nanosTimeout)
    throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    // 1.设置期限值
    final long deadline = System.nanoTime() + nanosTimeout;
    // 2.创建新节点,并设置到队列尾部
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            // 3.获取前置节点,如果前置节点是head,则尝试获取锁操作
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            
            // 4.如果当前时间已经超过期限值,则直接返回false
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            // 5.没有超过期限值,也没有获取到锁的线程则进入休息状态
            // 休息时间为nanosTimeout
            if (shouldParkAfterFailedAcquire(p, node) &&
                // 如果剩余时间超过1秒钟的话,就睡
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            // 6.接收中断响应,发现线程被中断,则直接抛出异常
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

    3)ReentrantLock.lockInterruptibly()可中断锁,在尝试加锁的过程中,如果线程被中断,则直接返回

// ReentrantLock.lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

// AbstractQueuedSynchronizer.acquireInterruptibly()
public final void acquireInterruptibly(int arg)
    throws InterruptedException {
    // 1.接收中断响应,发现线程被中断,则直接抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

// Sync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
    // 直接调用了nonfairTryAcquire()方法,在1)中我们已经分析了
    return nonfairTryAcquire(acquires);
}

// AbstractQueuedSynchronizer.doAcquireInterruptibly()
private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    // 1.创建新节点,并添加到队列尾部
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            // 2.获取前置节点,如果前置节点是head,则尝试获取锁操作
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            // 3.也没有获取到锁的线程则进入休息状态
            // parkAndCheckInterrupt()方法返回线程的中断状态,如果发现线程已经被中断,则直接抛出异常
            // 否则线程就进入休息状态,等待唤醒
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

// AbstractQueuedSynchronizer.parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

    总结:以上三种加锁方式的区别

    1)tryLock() 直接使用CAS设置state=1,成功就成功,不成功就拉倒

    2)tryLock(long time,TimeUnit unit) 先去竞争锁,不成功的话,则进入队列等待,休息规定时间后,醒来再去获取锁,不成功就拉倒

    3)lockInterruptibly() 先查询线程中断状态,及时响应中断;然后去竞争锁,失败的话,则进入队列等待,休息的同时检测线程中断状态

5.公平锁(FairSync)的处理方式

    上述看的都是关于非公平锁的分析,我们来简单看下公平锁是如何竞争资源的

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    // lock也是调用AQS.lock()方法
    // 这个不再分析
    final void lock() {
        acquire(1);
    }

    // AQS.lock方法会调用FairSync.tryAcquire()方法
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 1.state=0代表没有线程获取锁
        if (c == 0) {
            // 判断当前线程节点之前是否还有其他节点在等待
            // 如果没有才去尝试获取锁
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 这里的处理与NonFairSync一样
        // 也是判断获取锁的线程是否当前线程,如果是当前线程,则将state+1
        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()
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

    非公平锁和公平锁最主要的区别就是:如果state=0时(也就是没有其他线程获取锁),公平锁需要先判断当前线程节点之前是否还有其他节点,如果有,则不去竞争锁;非公平锁则不关心这个,一旦发现state=0,则直接使用CAS获取锁

发布了124 篇原创文章 · 获赞 126 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_26323323/article/details/86496917