Java读写锁源码解析

ReentrantReadWriteLock读写锁类结构图:

AQS如何用一个int值表示读写锁的2种状态?

    在AQS的实现类ReentrantReadWriteLock$Sync中,将32位int类型的state分成2部分,高位的16个2进制位表示读锁,低位的16个2进制位表示写锁。即读锁和写锁的并发最多只能达到2的16次方减去1。

    0000000000000000   0000000000000000

    ---------------------------------------------------------------------------------

    如果是读锁进入,则变成:

    0000000000000001    0000000000000000

    即将原有的state值加上65535(即2的16次方-1)

    如何获取读锁数量:

    将state的高位右移16位,则变成:

    0000000000000000   0000000000000001

    -------------------------------------------------------------------------------------

    如果是写锁进入,则变成:

    0000000000000000   0000000000000001

    即将原有的state值加1

    如何获取写锁数量:

    将state高位并上16个0,低位并上16个1(2的16次方减1),0&0或者1始终都为0,即将高位变成0,1&1为1,则低位并上16个1值不变

    0000000000000000    0000000000000001

    0000000000000000    1111 1111 1111 1111

   =0000000000000000    0000000000000001

对应的源码如下:

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;//写锁的最大并发量
//获取读锁数量  将state的高位右移16位
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
//获取写锁数量  将state的2进制并上16个1(2的16次方减1)
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

读锁重入实现原理:

ReentrantLock$Sync中的AQS的state表示的是重入次数,因为这个锁是独占锁,只有获取到锁的线程才能执行,才能重入,其他线程只能等待锁释放后去抢占或者排队等候

而ReentrantReadWriteLock$Sync中,AQS的state不在表示重入次数,它表示的是当前有多少读锁和多少写锁,假设没有写锁,那么所有的读锁都能并发访问数据;如果有写锁在排队,则后来的读锁、写锁也需要排队,因为读写互斥、写写互斥

在ReentrantReadWriteLock$Sync中,有一个ThreadLocalHoldCounter类型的属性,该属性是ThreadLocal<HoldCounter>的子类,而HoldCounter类型才是记录线程重入次数的类。具体源码如下:

private transient ThreadLocalHoldCounter readHolds;
static final class ThreadLocalHoldCounter
    extends ThreadLocal<HoldCounter> {
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}
static final class HoldCounter {
    int count = 0;
    // Use id, not reference, to avoid garbage retention
    final long tid = getThreadId(Thread.currentThread());
}

读锁源码解析:

ReadWriteLock rwlock=new ReentrantReadWriteLock();//不带参数或者参数为false,表示非公平锁
Lock rlock=rwlock.readLock();//获取读锁
new Thread(()->{
	rlock.lock();
			
	rlock.unlock();
}).start();


// ReentrantReadWriteLock  ReadLock
public void lock() {
     sync.acquireShared(1);//调用sync尝试获取读锁
}
//AQS
public final void acquireShared(int arg) {
    //tryAcquireShared是一个抽象方法
    //尝试获取锁,如果返回值为-1表示没有获取到锁,如果返回值为1表示获取到了锁
    if (tryAcquireShared(arg) < 0)
        //将当前线程放到队列中
        doAcquireShared(arg);
}
//ReentrantReadWriteLock Sycn
/**
* 1.如果写锁被另外一个线程持有,则返回-1
* 2.如果获取到了锁,则返回1
* 3.步骤2失败,则循环重试
**/
protected final int tryAcquireShared(int unused) {
   
    Thread current = Thread.currentThread();
    int c = getState();//获取状态
    //如果写锁被另外一个线程持有,则返回-1
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    int r = sharedCount(c);
    //公平锁时:如果队列为空或者队列第一个节点是当前线程,返回false
    //         如果队列队列不为空,则返回true,表示要规规矩矩的排队
    //非公平锁时:如果队列为空或者前面没有写锁在排队时,则返回false
    //           如果头结点不为空,下一个结点也不为空并且是写锁时,返回true,表示要规规矩矩的排队
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
            //记录读锁重入次数相关操作
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}


// ReentrantReadWriteLock  ReadLock
public void unlock() {
    sync.releaseShared(1);
}
//AQS
public final boolean releaseShared(int arg) {
    //抽象方法
    if (tryReleaseShared(arg)) {//尝试释放读锁,该方法主要是在操纵重入次数,如果次数为0,则返回true
        doReleaseShared();
        return true;
    }
    return false;
}
//ReentrantReadWriteLock Sync
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    //如果当前线程是第一个读锁
    if (firstReader == current) {
        // 如果重入次数为1,表示没有重入过
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else//如果重入过,则重入次数减一
            firstReaderHoldCount--;
    } else {
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        //获取到重入次数
        int count = rh.count;
        if (count <= 1) {
            //如果重入次数为1,表示可以真正的释放锁了
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        //如果重入过,则重入次数减一
        --rh.count;
    }
    for (;;) {
        int c = getState();
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

写锁源码解析:

ReadWriteLock rwlock=new ReentrantReadWriteLock();
Lock wlock=rwlock.writeLock();
new Thread(()->{
	wlock.lock();
	
	wlock.unlock();
}).start();

//WriteLock
public void lock() {
    sync.acquire(1);
}
//AQS
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&//尝试获取写锁
        //这里调用的是AQS的排队相关方法,因为写锁是互斥的
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
//ReentrantReadWriteLock$Sync
protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);//获取写锁数量
    //如果AQS的state状态不为0,表示队列中有锁,可能是读锁,也可能是写锁
    if (c != 0) {
        //如果写锁为0 或者当前线程不是排队中写锁的线程,返回false
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        //表示当前的写锁是重入进来的,那么返回true
        setState(c + acquires);
        return true;
    }
    //表示队列中没有锁,这个写锁是第一个锁
    //公平锁时:如果队列为空或者队列第一个节点是当前线程,返回false
    //         如果队列队列不为空,则返回true,表示要规规矩矩的排队
    //非公平锁:直接返回false
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))//当前锁的CAS释放失败
        return false;
    setExclusiveOwnerThread(current);
    //返回true表示获取到了写锁
    return true;
}

总结:

1、读写锁适合读多写少的场景,读读并行,读写、写写互斥

2、读写锁使用一个int类型的state变量,实现了2种锁状态,低16位记录写锁数量,高16位记录读锁数量,而不是记录的重入次数。获取读锁数量时,需要将state的2进制位右移16位;获取写锁数量时,需要将state的2进制位与上前16位0后16位1

3、在读锁或者写锁中,需要记录某个线程的重入次数,使用到了ThreadLocalHoldCounter类,该类是ThreadLocal<HoldCounter>的子类,而HoldCounter类型才是记录线程重入次数的类。

4、不管是读锁还是写锁,在获取锁的时候,都有公平或非公平2种方式,这取决于创建ReentrantReadWriteLock的创建方式

5、读锁加锁过程:

    公平锁时:

        如果队列不为空且当前线程不是队列的首结点,则表示要规规矩矩的排队,否则执行后面的CAS操作

    非公平锁时:

        如果头结点不为空,下一个结点是写锁时,表示要规规矩矩的排队;否则执行CAS操作

6、写锁加锁过程:如果队列中有读锁或者写锁时,需要排队,如果队列为空,则直接获取到写锁

     公平锁时:

             如果队列不为空且当前线程不是队列的第一个结点,则表示要规规矩矩的排队,否则执行后面的CAS操作

     非公平锁:

            如果队列为空时,直接执行CAS操作

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值