Java并发之ReentrantLock

Lock介绍

Lock在Java中是一个接口,在这个接口中仅仅定义了6个方法:

public interface Lock {

    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    void unlock();

    Condition newCondition();
}

实现了Lock接口的类有:ReentrantLock、ReentrantReadWriteLock(ReentrantReadWriteLock实际上是实现了ReadWriteLock,但是其内部类ReadLock和WriteLock实现了Lock接口)

我们这里讲的是ReentrantLock,ReentrantLock是一个实现了Lock接口的可重入锁,它具有与使用synchronized修饰的语句一样的功能,具有同步的作用;同时ReentrantLock具有可重入的特性。

ReentrantLock源码分析

我们来看下ReentrantLock的源码,ReentrantLock实现了Lock接口和Serializable接口:

public class ReentrantLock implements Lock, java.io.Serializable {
    ......
}

构造方法,ReentrantLock有两个构造方法:

public ReentrantLock() {
        sync = new NonfairSync();
    }

public ReentrantLock(boolean fair) {
    //是否为公平锁
    sync = fair ? new FairSync() : new NonfairSync();
}

有参数的构造方法参数是标志这个lock的所是否为公平锁,默认情况下ReentrantLock为非公平锁。

ReentrantLock中有三个内部类:Sync、NonfairSync、FairSync;NonfairSync和FairSync类是Sync的子类,而Sync继承自AbstractQueuedSynchronizer抽象类,Java中的两个lock和部分并发容器都是基于AQS实现的。

然后我们来看下ReentrantLock的lock()方法的源代码:

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

Sync中的lock方法是个抽象方法,并没有实现,具体的实现交由子类实现;
Sync中的lock方法:

abstract void lock();

公平锁

lock()方法直接调用sync.lock()实现的,而sync变量在实例化ReentrantLock时实例化了,对于公平锁的lock:

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

直接调用AbstractQueuedSynchronizer的acquire()方法,AbstractQueuedSynchronizer的acquire()方法的实现如下:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

公平锁中重了写父类的tryAcquire():

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;                                   
}

首先判断当前线程是否在队列队头或者线程队列是否为空并且可以将线程状态设置为acquires的值,则将当前运行线程设置为当前线程并返回true,加锁成功,否则判断获取到锁的线程是不是当前线程,是的话,将线程持有锁加一(锁的可重入)并返回true,加锁成功,以上情况都不满足,加锁失败。

当加锁失败时,执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg),addWaiter方法将当前线程使用指定模式添加至等待节点,Node.EXCLUSIVE为独占模式,Node.SHARED为共享模式。

当加锁失败,并且将等待线程添加至等待链表后,执行acquireQueued()方法。

非公平锁

对于非公平锁的lock:

final void lock() {
    //持有0个锁,直接尝试加锁
    if (compareAndSetState(0, 1))                       
        //成功设置运行线程为当前线程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        //已经有线程持有锁了,加锁失败
        acquire(1);                                     
}

首先compareAndSetState(0, 1)直接尝试加锁,若加锁成功,设置当前运行线程为当前线程,如果失败的话,运行acquire(1);

acquire()方法:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

非公平锁与公平锁一样实现了tryAcquire()方法:

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

nonfairTryAcquire()方法的实现:

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;                                  
}

nonfairTryAcquire()方法实现与公平锁的tryAcquire()类似,不同的是公平锁需要判断当前阻塞的线程队列中当前尝试加锁的线程是否是对头和队列是否为空,而非公平锁所有线程直接尝试加锁,谁能拿到谁就加锁成功。

释放锁

unLock()方法源码:

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

直接调用AQS的实现,release源码:

public final boolean release(int arg) {
    //尝试解锁,tryRelease交由子类实现
    if (tryRelease(arg)) {
        //获取头节点
        Node h = head;
        //判断头节点是否为空,并且线程状态是否为初始值
        if (h != null && h.waitStatus != 0)
            //唤醒节点的后置节点
            unparkSuccessor(h);
        //解锁成功
        return true;
    }
    //失败
    return false;
}

tryRelease方法在Sync子类中实现,源码:

protected final boolean tryRelease(int releases) {
    //线程状态新值,锁可重入,新状态为老状态减releases
    int c = getState() - releases;
    //尝试解锁的线程是否为当前执行线程
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //为0时,所有的锁都已经释放
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //更新持有锁数量
    setState(c);
    return free;
}

Condition

Lock可以通过lock.newCondition()来获取一个Condition,Condition是一个接口,接口中定义了await()、awaitNanos()、awaitUninterruptibly()、awaitUntil()、signal()、signalAll()这几个方法,await方法相当于Object中的wait,而signal,signalAll方法相当于Object中的notify和notifyAll方法。Object中的线程同步方法常用于synchronized中;await()、awaitNanos()、awaitUninterruptibly()、awaitUntil()、signal()、signalAll()方法用于lock.lock()代码段中对Lock进行线程之间的同步。

posted @ 2020-11-05 23:22  Sirius-  阅读(90)  评论(0编辑  收藏  举报