Java并发之ReentrantReadWriteLock

ReadWriteLock

ReadWriteLock是一个读写锁接口,所谓读写锁,是对访问资源共享锁和互斥锁,一般的重入性语义为如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。

ReadWriteLock接口中定义了两个方法:

public interface ReadWriteLock {
    
    Lock readLock();

    Lock writeLock();
}

ReentrantReadWriteLock源码分析

ReentrantReadWriteLock实现了ReadWriteLock接口和Serializable接口:

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    ......
}

构造方法:

public ReentrantReadWriteLock() {
    //调用有参数构造方法实现
    this(false);
}

public ReentrantReadWriteLock(boolean fair) {
    //是否是公平锁
    sync = fair ? new FairSync() : new NonfairSync();
    //读锁
    readerLock = new ReadLock(this);
    //写锁
    writerLock = new WriteLock(this);
}

默认情况下,ReentrantReadWriteLock为非公平锁。

ReentrantReadWriteLock中有五个内部类,分别是:ReadLock、WriteLock、FairSync、NonfairSync、Sync,其中Sync继承自AQS类,FairSync、NonfairSync继承自Sync类。

ReentrantReadWriteLock读写锁同步状态通过一个整型位来表示状态,高16位表是读状态,低16位表是写状态;

static final int SHARED_SHIFT   = 16;
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//返回共享锁数量
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
//返回独占锁数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

读写锁状态

读锁

lock

我们先看下读锁,读锁的lock方法源码:

public void lock() {
    //调用AQS类acquireShared方法
    sync.acquireShared(1);
}

AQS类中acquireShared源码:

public final void acquireShared(int arg) {
    //尝试加锁
    if (tryAcquireShared(arg) < 0)
        //加锁失败,阻塞住
        doAcquireShared(arg);
}

在之前的文章中分析AQS类我们说过tryAcquireShared方法在AQS类中是无法直接调用的,Sync类中实现了tryAcquireShared方法,源码:

protected final int tryAcquireShared(int unused) {
    //获取当前线程
    Thread current = Thread.currentThread();
    //获取线程同步状态
    int c = getState();
    //计算写锁是否为0并且当不为0时判断持锁线程是否为当前线程
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        //存在写锁,并且写锁持有者不为当前线程,加锁失败
        return -1;
    //没有线程持有写锁,获取持有读锁数
    int r = sharedCount(c);
    //readerShouldBlock():读锁是否需要等待,r < MAX_COUNT:持有线程小于最大数(65535),compareAndSetState(c, c + SHARED_UNIT):设置读取锁状态
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        //持有读锁数为0
        if (r == 0) {
            //获取读锁的第一个线程设置为当前线程
            firstReader = current;
            //firstReader持有读锁数量为1
            firstReaderHoldCount = 1;
        //持有读锁数不为0,且持有读锁线程为当前线程
        } else if (firstReader == current) {
            //将firstReader持有读锁数量加1
            firstReaderHoldCount++;
        //持有读锁数不为0,且持有线程不为当前线程
        } else {
            //获取HoldCounter
            HoldCounter rh = cachedHoldCounter;
            //HoldCounter为空,或者获取的HoldCounter不是当前线程的HoldCounter
            if (rh == null || rh.tid != getThreadId(current))
                //重新获取HoldCounter并设置rh和cachedHoldCounter
                cachedHoldCounter = rh = readHolds.get();
            //rh不为空,且rh的值为0
            else if (rh.count == 0)
                //设置线程自有的HoldCounter
                readHolds.set(rh);
            //持有读锁加1
            rh.count++;
        }
        //成功加锁
        return 1;
    }
    //自旋尝试加锁
    return fullTryAcquireShared(current);
}

readerShouldBlock()在Sync中定义为抽象类,由子类FairSync或NonfairSync实现;

对于FairSync中readerShouldBlock方法:

final boolean readerShouldBlock() {
    //调用AQS类hasQueuedPredecessors实现
    return hasQueuedPredecessors();
}

//AQS类hasQueuedPredecessors方法
public final boolean hasQueuedPredecessors() {
    Node t = tail; 
    Node h = head;
    Node s;
    //判断队头线程是否为当前线程,是的话返回false,否则返回true
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

对于NonfairSync中的readerShouldBlock方法:

final boolean readerShouldBlock() {
    //调用AQS类中apparentlyFirstQueuedIsExclusive实现            
    return apparentlyFirstQueuedIsExclusive();
}

//AQS类apparentlyFirstQueuedIsExclusive方法
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    //链表头不为空,当前运行线程不为空,运行线程为独占模式返回true,否则返回false
    return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}

在Sync类中,还有两个内部类,分别是:HoldCounter、ThreadLocalHoldCounter,HoldCounter类有两个成员变量:count和tid,其中count是用来计数线程持有锁的数量而tid是通过一个线程的id来标志是哪个线程的HoldCounter,HoldCounter保存在每个线程的ThreadLocal中;ThreadLocalHoldCounter是ThreadLocal的子类用于维护每个线程自己的HoldCounter。

unLock

读锁中unLock源码:

public void unlock() {
    //调用AQS类实现
    sync.releaseShared(1);
}

//AQS类releaseShared方法
public final boolean releaseShared(int arg) {
    //尝试释放锁,交由子类实现
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

//Sync类中tryReleaseShared方法
protected final boolean tryReleaseShared(int unused) {
    //获取当前线程
    Thread current = Thread.currentThread();
    //判断持有读锁线程是否为当前线程
    if (firstReader == current) {
        //判断持有读锁数量是否为1
        if (firstReaderHoldCount == 1)
            //为1,置空firstReader
            firstReader = null;
        else
            //不为1,将该线程持有锁数量减1
            firstReaderHoldCount--;
    //持有锁线程不为当前线程
    } else {
        //获取到该线程的HoldCounter
        HoldCounter rh = cachedHoldCounter;
        //判断HoldCounter是否为空,且是否为该线程的HoldCounter
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        //获取该线程持有的锁数量
        int count = rh.count;
        //判断是否小于1
        if (count <= 1) {
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        //将持有锁数量减1
        --rh.count;
    }
    //自旋确保成功
    for (;;) {
        //获取状态
        int c = getState();
        //更新状态值
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

当执行tryReleaseShared返回true后执行doReleaseShared方法,doReleaseShared方法的源码我们在AQS类分析的时候已经讲过了,故此不再赘述。

写锁

lock

写锁中的lock源码:

public void lock() {
    //调用AQS类实现
    sync.acquire(1);
}

//AQS类中acquire方法
public final void acquire(int arg) {
    //tryAcquire由子类实现
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法在AQS类分析的时候已经分析过了。

我们来看下子类Sync中实现的tryAcquire方法:

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    //获取线程状态
    int c = getState();
    //获取写锁数量
    int w = exclusiveCount(c);
    if (c != 0) {
        //有线程持有写锁,且不是当前线程
        if (w == 0 || current != getExclusiveOwnerThread())
            //加锁失败
            return false;
        //判断是否超过最大值
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
        //CAS更新持锁线程数量
        setState(c + acquires);
        //加锁成功
        return true;
    }
    //没有线程持有写锁,判断是否应该阻塞该线程并尝试更新持锁线程数量
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        //应该阻塞,或者更新持锁线程数量失败
        return false;
    //成功,设置运行线程为当前线程
    setExclusiveOwnerThread(current);
    //加锁成功
    return true;
}

writerShouldBlock方法分公平锁与非公平锁实现;

先来看下非公平锁实现:

final boolean writerShouldBlock() {
    return false; // writers can always barge
}

公平锁writerShouldBlock实现:

final boolean writerShouldBlock() {
    //调用AQS实现,hasQueuedPredecessors方法在前面已经讲述
    return hasQueuedPredecessors();
}

unLock

public void unlock() {
    //调用AQS的release方法实现
    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在AQS子类Sync中实现:

protected final boolean tryRelease(int releases) {
    //判断是否是当前线程执行unLock
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int nextc = getState() - releases;
    //判断读锁数量是否应该为0
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        //为0,置空当前执行线程
        setExclusiveOwnerThread(null);
    //更新线程持有写锁数量
    setState(nextc);
    return free;
}

锁降级

锁降级指的是写锁降级成为读锁,如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这个过程是不能被称为锁降级的。锁降级是指把当前拥有的写锁,再获取到读锁,随后释放写锁的过程。

ReentrantReadWriteLock不支持锁升级(持有读锁,获取写锁,最后释放读锁的过程),目的也是为了数据可见性,如果读锁已经被多个线程获取,其中任意线程成功获取了写锁并且更新了数据,这个更新对其他已经获取到读锁的线程是不可见的。

posted @ 2020-11-05 23:24  Sirius-  阅读(136)  评论(0)    收藏  举报