【< J.U.C>】
这里写目录标题
- 【< J.U.C>】
- [1] AQS
- [2] ReentrantLock
- [3] ReentrantReadWriteLock
- [4] StampedLock
- [5] Semaphore
- [6] threadlocal
- [7] CountDownLatch
- [8] CyclicBarrier?
- [9] Atmoic
- [10] FutureTask
- [11] ForkJoin
【< J.U.C>】
[1] AQS
见专题
[2] ReentrantLock
见专题
[3] ReentrantReadWriteLock
https://www.cnblogs.com/xiaoxi/p/9140541.html
现实中有这样一种场景:**对共享资源有读和写的操作,且写操作没有读操作那么频繁。**在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。
针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它表示两个锁(ReadLock和WriteLock),一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁,描述如下:
- 线程进入读锁(共享)的前提条件:
没有其他线程的写锁,
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
- 线程进入写锁(互斥)的前提条件:
没有其他线程的读锁
没有其他线程的写锁
- 而读写锁有以下六个重要的特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁
(4)不支持锁升级:为了保证内存可见性
(5) 不支持conditon函数
(6) 读写锁允许多个读线程访问,但是写线程时,不允许其他线程。
1. 读写状态的设计
如何用一个state值来表示读写两种状态呢?
同步状态在重入锁的实现中是表示被同一个线程重复获取的次数,即一个整型变量来维护,但是之前的那个表示仅仅表示是否锁定,而不用区分是读锁还是写锁。而读写锁需要在同步状态(一个整形变量)上维护多个读线程和一个写线程的状态。
读写锁对于同步状态的实现是在一个整形变量上通过“按位切割使用”:将变量切割成两部分,高16位表示读,低16位表示写。
假设当前同步状态值为S,get和set的操作如下:
(1)获取写状态:
S&0x0000FFFF:将高16位全部抹去
(2)获取读状态:
S>>>16:无符号补0,右移16位
(3)写状态加1:
S+1
(4)读状态加1:
S+(1<<16)即S + 0x00010000
在代码层的判断中,如果S不等于0,当写状态(S&0x0000FFFF),而读状态(S>>>16)大于0,则表示该读写锁的读锁已被获取。
4、写锁的获取与释放
看下WriteLock类中的lock和unlock方法:
public void lock() {sync.acquire(1);
}public void unlock() {sync.release(1);
}
可以看到就是调用的独占式同步状态的获取与释放,因此真实的实现就是Sync的 tryAcquire和 tryRelease。
写锁的获取,看下tryAcquire:
1 protected final boolean tryAcquire(int acquires) {2 //当前线程3 Thread current = Thread.currentThread();4 //获取状态5 int c = getState();6 //写线程数量(即获取独占锁的重入数)7 int w = exclusiveCount(c);8 9 //当前同步状态state != 0,说明已经有其他线程获取了读锁或写锁
10 if (c != 0) {
11 // 当前state不为0,此时:如果写锁状态为0说明读锁此时被占用返回false;
12 // 如果写锁状态不为0且写锁没有被当前线程持有返回false
13 if (w == 0 || current != getExclusiveOwnerThread())
14 return false;
15
16 //判断同一线程获取写锁是否超过最大次数(65535),支持可重入
17 if (w + exclusiveCount(acquires) > MAX_COUNT)
18 throw new Error("Maximum lock count exceeded");
19 //更新状态
20 //此时当前线程已持有写锁,现在是重入,所以只需要修改锁的数量即可。
21 setState(c + acquires);
22 return true;
23 }
24
25 //到这里说明此时c=0,读锁和写锁都没有被获取
26 //writerShouldBlock表示是否阻塞
27 if (writerShouldBlock() ||
28 !compareAndSetState(c, c + acquires))
29 return false;
30
31 //设置锁为当前线程所有
32 setExclusiveOwnerThread(current);
33 return true;
34 }
其中exclusiveCount方法表示占有写锁的线程数量,源码如下:
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
说明:直接将状态state和(2^16 - 1)做与运算,其等效于将state模上2^16。写锁数量由state的低十六位表示。
从源代码可以看出,获取写锁的步骤如下:
(1)首先获取c、w。c表示当前锁状态;w表示写线程数量。然后判断同步状态state是否为0。如果state!=0,说明已经有其他线程获取了读锁或写锁,执行(2);否则执行(5)。
(2)如果锁状态不为零(c != 0),而写锁的状态为0(w = 0),说明读锁此时被其他线程占用,所以当前线程不能获取写锁,自然返回false。或者锁状态不为零,而写锁的状态也不为0,但是获取写锁的线程不是当前线程,则当前线程也不能获取写锁。
(3)判断当前线程获取写锁是否超过最大次数,若超过,抛异常,反之更新同步状态(此时当前线程已获取写锁,更新是线程安全的),返回true。
(4)如果state为0,此时读锁或写锁都没有被获取,判断是否需要阻塞(公平和非公平方式实现不同),在非公平策略下总是不会被阻塞,在公平策略下会进行判断(判断同步队列中是否有等待时间更长的线程,若存在,则需要被阻塞,否则,无需阻塞),如果不需要阻塞,则CAS更新同步状态,若CAS成功则返回true,失败则说明锁被别的线程抢去了,返回false。如果需要阻塞则也返回false。
(5)成功获取写锁后,将当前线程设置为占有写锁的线程,返回true。
方法流程图如下:
写锁的释放,tryRelease方法:
[](javascript:void(0)
【< J.U.C>】相关推荐
- 【SPRS J P RS 2022】小目标检测模块:A Normalized Gaussian Wasserstein Distance for Tiny Object Detection
[SPRS J P & RS 2022]A Normalized Gaussian Wasserstein Distance for Tiny Object Detection A Norma ...
- Python(分治算法)问题 F: 求逆序对_给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目。
问题 F: 求逆序对 题目描述 给定一个序列a1,a2,-,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目. 注意:n<=10^5,ai<=10^5 ...
- <From Zero to Hero>零基础学习Python基础语法【条件判断与条件嵌套】
目录 条件判断 单向判断:if 双向判断:if-else- 多向判断:if-elif-else- if嵌套 if嵌套的执行顺序 如何写嵌套代码 小练习1 小练习2 对于Python来讲,需要正确的[沟 ...
- java加载c库阻塞_【死磕Java並發】-----J.U.C之阻塞隊列:DelayQueue
DelayQueue是一個支持延時獲取元素的無界阻塞隊列.里面的元素全部都是"可延期"的元素,列頭的元素是最先"到期"的元素,如果隊列里面沒有元素到期,是不能從 ...
- 【云原生微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式
文章目录 一.前言 二.Gateway集成Sentinel API 0.集成Sentinel的核心概念 1)GatewayFlowRule 和 ApiDefinition 2)GatewayFlowR ...
- NLP-词向量-发展:词袋模型【onehot、tf-idf】 -> 主题模型【LSA、LDA】 -> 词向量静态表征【Word2vec、GloVe、FastText】 -> 词向量动态表征【Bert】
NLP-词向量-发展: 词袋模型[onehot.tf-idf] 主题模型[LSA.LDA] 基于词向量的静态表征[Word2vec.GloVe.FastText] 基于词向量的动态表征[Bert] 一 ...
- SWPU-DS)若有 n 阶对称矩阵 A,以行序为主序方式,将其下三角形的元素(包括主对角线上所有元素)依次存放于一维数组B[1..(n(n+1))/2]中,则在 B 中确定 a[i, j](i<j)
SWPU-DS)若有 n 阶对称矩阵 A,以行序为主序方式,将其下三角形的元素(包括主对角线上所有元素)依次存放于一维数组B[1-(n(n+1))/2]中,则在 B 中确定 a[i, j](i< ...
- java核心技术 基础知识<集合并发part1>
文章目录 java核心技术 基础知识<集合&并发part1> 9 泛型程序设计 9.5 算法 9.6 遗留的集合 14 并发 14.2 中断线程 14.3 线程状态 14.4 线程 ...
- 【一些好听的英文歌曲】
原文地址:[一些好听的英文歌曲]作者:姑娘我笑着活 1.Bubbly--Colbie Caillat(你听过一遍就会非常喜欢的歌): 2.Burning--Maria Arredondo: 3.Hap ...
最新文章
- 图解Oracle同义词
- java字节码运行原理_JVM 内部原理(六)— Java 字节码基础之一
- 电脑常见的VGA、DVI、PS/2、USB等接口知识笔记,值得收藏!
- [LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史
- ssl2346-联络员【图论,最小生成树】
- 阿里云贾扬清:数据湖正成为企业数据应用创新标配
- (转)Spring4.2.5+Hibernate4.3.11组合开发
- 【OpenCV入门指南】第八篇 灰度直方图
- BFGS优化算法简介
- 哈夫曼树及哈夫曼编码例题
- vmware fusion 7 序列号
- 【论文阅读】DNS隧道攻击检测算法整合
- 我们如何学习:学会学习再学习
- android打电话的intent,如何在Android中使用intent打电话?
- ubuntu设置相机为固定焦距
- hsk内网穿透+SERV-U+搭建FTP服务器+并实现外网访问
- python图像锐化 增强边缘_[Python图像处理]十一.图像锐化与边缘检测之Roberts算子、Prewitt算子、Sobel算子和Laplacian算子,Schar算子...
- Linux磁盘挂载问题 ***is apparently in use by the system; will not make a filesystem here!
- 浅谈代码规范基础调试几道面试题
- 组态王与西门子S7 200 SMART连接