福建省建设监理网官方网站,手机品牌排行榜,电子商务网站开发进什么科目,wordpress登陆好慢AQS之ReentrantReadWriteLock
一. 归纳总结 ReentrantReadWriteLock适合读多写少的场景。是可重入的读写锁实现类。其中, 写锁是独占的#xff0c;读锁是共享的。 支持锁降级#xff08;持有写锁、获取读锁#xff0c;最后释放写锁的过程#xff09;
锁降级可以帮助我们…AQS之ReentrantReadWriteLock
一. 归纳总结 ReentrantReadWriteLock适合读多写少的场景。是可重入的读写锁实现类。其中, 写锁是独占的读锁是共享的。 支持锁降级持有写锁、获取读锁最后释放写锁的过程
锁降级可以帮助我们拿到当前线程修改后的结果而不被其他线程所破坏防止更新丢失。可以保证数据的可见性如果当前线程不获取读锁而是直接释放写锁假设此刻另一个线程记作线程T获取了写锁并修改了数据那么当前线程无法感知线程T的数据更新。如果当前线程获取读锁即遵循锁降级的步骤则线程T将会被阻塞直到当前线程使用数据并释放读锁之后线程T才能获取写锁进行数据更新。
不支持锁升级持有读锁、获取写锁最后释放读锁的过程 目的也是保证数据可见性如果读锁已被多个线程获取其中任意线程成功获取了写锁并更新了数据则其更新对其他获取到读锁的线程是不可见的。
二.读写状态的设计
读写锁对于同步状态的实现是在一个整形变量上通过“按位切割使用”将变量切割成两部分高16位表示读低16位表示写。 假设当前同步状态值为Sget和set的操作如下
1、获取写状态
S0x0000FFFF:将高16位全部抹去2、获取读状态
S16:无符号补0右移16位3、写状态加1 S14、读状态加1
S116即S 0x00010000
在代码层面的判断中同步状态S不等于0时当写状态S0x0000FFFF等于0时则读状态S16大于0即读锁已被获取。
三.写锁的获取与释放
3.1 写锁的获取
写锁是一个支持重进入的排它锁。如果当前线程已经获取了写锁则增加写状态。如果当前线程在获取写锁时读锁已经被获取读状态不为0或者该线程不是已经获取写锁的线程 则当前线程进入等待状态。写锁的获取是通过重写AQS中的tryAcquire方法实现的。
protected final boolean tryAcquire(int acquires) {//当前线程Thread current Thread.currentThread();//获取state状态 存在读锁或者写锁状态就不为0int c getState();//获取写锁的重入数int w exclusiveCount(c);//当前同步状态state ! 0说明已经有其他线程获取了读锁或写锁if (c ! 0) {// c!0 w0 表示存在读锁// 当前存在读锁或者写锁已经被其他写线程获取则写锁获取失败if (w 0 || current ! getExclusiveOwnerThread())return false;// 超出最大范围 65535if (w exclusiveCount(acquires) MAX_COUNT)throw new Error(Maximum lock count exceeded);//同步state状态setState(c acquires);return true;}// writerShouldBlock有公平与非公平的实现, 非公平返回false会尝试通过cas加锁//c0 写锁未被任何线程获取当前线程是否阻塞或者cas尝试获取锁 if (writerShouldBlock() ||!compareAndSetState(c, c acquires))return false;//设置写锁为当前线程所有setExclusiveOwnerThread(current);return true;源码分析
读写互斥写写互斥写锁支持同一个线程重入writerShouldBlock写锁是否阻塞实现取决公平与非公平的策略FairSync和NonfairSync
3.2 读锁的获取
实现共享式同步组件的同步语义需要通过重写AQS的tryAcquireShared方法和tryReleaseShared方法。读锁的获取实现方法为
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);/*** 读锁是否阻塞 readerShouldBlock()公平与非公平的实现* r MAX_COUNT 持有读锁的线程小于最大数65535* compareAndSetState(c, c SHARED_UNIT) cas设置获取读锁线程的数量*/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);源码分析
读锁共享读读不互斥读锁可重入每个获取读锁的线程都会记录对应的重入数读写互斥锁降级场景除外支持锁降级持有写锁的线程可以获取读锁但是后续要记得把读锁和写锁读释放readerShouldBlock读锁是否阻塞实现取决公平与非公平的策略FairSync和NonfairSync