JavaSE(23)——ReentrantReadWriteLock源码

ReentrantReadWriteLock源码

1. 源码注释

在java中,synchronized内置锁ReentrantLock都是互斥锁,一次只能有一个线程进入到被临界区。

而ReentrantReadWriteLock是一个读写锁

  • 读取数据的时候,可以多个线程同时进入到临界区内
  • 写入数据的时候,无聊是读线程还是写线程,都是互斥的。

一般情况下,大多数都是读数据多,写数据少的情景。这种时候使用读写锁就非常合适了。

ReadWriteLock源码注释:

  1. 维护了一对锁
  2. 读锁可同时进入到临界区
  3. 写锁是独占的
  4. 读线程可以看到写线程更新过的数据 (内存同步)
  5. 一个写线程修改数据(互斥)
  6. 多个读线程并发访问共享数据
  7. 理论上,这种读写锁的并发性是比互斥锁要好的,但还是要看计算机的处理器和共享资源是否适合这种模式
  8. 一个集合要是初始化之后常常是查询数据、少量修改数据,那么是适合读写锁的
  9. 但是,当修改操作并发增加,那么读写锁的开销大部分都是互斥锁的了

大概说明了,在读的时候可以共享,在写的时候是互斥的。

ReentrantReadWriteLock源码注释:

  1. 实现了ReadWriteLock接口
  2. 这个类不会偏向读锁或写锁
  3. 默认是非公平锁,吞吐量一般比公平锁要搞
  4. 公平锁中,等待时间最长的写进程将获得写锁
  5. 当尝试公平获取读锁时,写锁释放后,读线程才能获取锁
  6. 可重入
  7. 写锁能获得读锁,读锁不能获得写锁
  8. 写锁可以降级成读锁,读锁不能升级为写锁。
  9. 读锁和写锁都支持在获取时中断
  10. 写锁支持条件对象,读锁不支持条件对象

总结:

  • 读锁不支持条件对象,写锁支持条件对象
  • 读锁不能升级为写锁,写锁可以降级为读锁
  • 读写锁也有公平和非公平模式
  • 读锁支持多个读线程进入临界区,写锁是互斥的

2. ReentrantReadWriteLock内部类

ReentrantReadWriteLock中比ReentrantLock多了两个内部类,来维护读锁和写锁,但是主体还是使用Syn:

  • WriteLock
  • ReadLock

3. 读锁和写锁的状态表示

在ReentrantLock锁上使用的是state来表示同步状态(或重入的次数),而在ReentrantReadWriteLock是这样代表读写状态的:

/**
* 将state值(int)的前16位作为读锁的state,后16位作为写锁的state
*/

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;

//通过位运算,拿到前16位是共享锁的数量
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
//后16位是独占锁的state
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

4. 写锁

  • 加锁
public void lock() {
    sync.acquire(1);
}

//arg:1
public final void acquire(int arg) {
    //返回true,则获取到锁,不需要执行&&后面的操作以及if成立的操作
    if (!tryAcquire(arg) &&
        //当tryAcquire获取不成功,则请求排队
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//acquires:1
protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) {
        //c!=0 && w ==0,则读锁数量不为0
        //存在读锁时,写锁与读锁互斥
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        //当加锁后锁数量达到上限,则error
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        //重入加锁(写锁)
        setState(c + acquires);
        return true;
    }
    
    //c==0,则当前无锁
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    //设置当前线程持有锁
    setExclusiveOwnerThread(current);
    return true;
}

//公平锁
final boolean writerShouldBlock() {
    //判断队列中是否有等待节点
    return hasQueuedPredecessors();
}

//非公平锁
final boolean writerShouldBlock() {
    //写锁一直可以插队
    return false;
}
  • 解锁
public void unlock() {
    sync.release(1);
}

//AQS中的方法
//arg:1
public final boolean release(int arg) {
    //返回true,则解锁后线程空闲
    if (tryRelease(arg)) {
        Node h = head;
        //如果队列中有等待线程,则唤醒后继线程
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

//releases:1
protected final boolean tryRelease(int releases) {
    //判断当前线程是否为持有锁的线程
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    
    //nextc:解锁后的state
    int nextc = getState() - releases;
    //如果解锁后state为0,则锁空闲
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        //设置持有锁的线程为null
        setExclusiveOwnerThread(null);
    //否则,解锁重入锁
    setState(nextc);
    return free;
}

5. 读锁

  • 加锁
public void lock() {
    sync.acquireShared(1);
}

//AQS中的方法
//arg:1
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

//unused:1
protected final int tryAcquireShared(int unused) {

    Thread current = Thread.currentThread();
    int c = getState();
    
    //当写锁不为0且当前线程不持有锁,返回-1
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    //r:读锁数量
    int r = sharedCount(c);
    
    //readerShouldBlock
    //在已存在读锁的情况下,就会返回false
    //队列中的后继节点为写锁情况下,需要加锁
    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;
    }
    //CAS失败后再次尝试获取读锁
    return fullTryAcquireShared(current);
}

//再次尝试获取读锁
//每个线程各自获取读锁的次数只能选择保存在ThreadLocal中
final int fullTryAcquireShared(Tread current) {
    
    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                return -1;
        } else if (readerShouldBlock()) {
            // Make sure we're not acquiring read lock reentrantly
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
            } else {
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                if (rh.count == 0)
                    return -1;
            }
        }
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                if (rh == null)
                    rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
                cachedHoldCounter = rh; // cache for release
            }
            return 1;
        }
    }
}
  • 解锁
public void unlock() {
    sync.releaseShared(1);
}

//AQS方法
//arg:1
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

//unused:1
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    if (firstReader == current) {
        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;
        //读锁缓存<=1,则直接remove
        if (count <= 1) {
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        //计数器-1
        --rh.count;
    }
    //读锁数量-1
    for (;;) {
        int c = getState();
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

//当读锁清空,没有锁时,唤醒后继线程获取锁
private void doReleaseShared() {

    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}
  • 锁的降级
public void processData() {
	readLock.lock();
	if (!update) {// 如果未更新完成
		readLock.unlock();// 必须先释放读锁
		writeLock.lock();// 锁降级:1.获取写锁

		try{
		if (!update) {
			// 准备数据....略
			update = true;
		}
		readLock.lock();// 锁降级:2.获取读锁。写锁获取后这线程可以继续获取读锁,反之不行
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			writeLock.unlock(); 锁降级:3.释放写锁
		}
	}
	
	try{
		// 读取更新后的数据.........
	}finally{
		readLock.unlock();
	}

}

锁的降级:

同一个线程中,获取写锁-----获取读锁-----数据操作-----释放写锁-----释放读锁

降级目的:

为了保证吞吐性,以及可见性,线程A先获取写锁,更新数据,接着需要读取更新后的数据,获取读锁,释放写锁。

更新数据,到读取更新后数据这段时间里,由于加上了读锁,阻塞了其他的写锁操作数据,保证了数据的安全的同时增加了读锁的吞吐量

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值