我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java 重入锁和读写锁怎么使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java 重入锁和读写锁怎么使用

这篇文章主要介绍“Java 重入锁和读写锁怎么使用”,在日常操作中,相信很多人在Java 重入锁和读写锁怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java 重入锁和读写锁怎么使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

重入锁

重入锁 ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取锁时的公平和非公平性选择

所谓不支持重进入,可以考虑如下场景:当一个线程调用 lock() 方法获取锁之后,如果再次调用 lock() 方法,则该线程将会被自己阻塞,原因是在调用 tryAcquire(int acquires) 方法时会返回 false,从而导致线程阻塞

synchronize 关键字隐式的支持重进入,比如一个 synchronize 修饰的递归方法,在方法执行时,执行线程在获取锁之后仍能连续多次地获得该锁。ReentrantLock 虽然不能像 synchronize 关键字一样支持隐式的重进入,但在调用 lock() 方法时,已经获得锁的线程,能够再次调用 lock() 方法获取锁而不被阻塞

1. 实现重进入

重进入特性的实现需要解决以下两个问题:

线程再次获取锁
锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取

锁的最终释放
线程重复 n 次获取锁,随后在第 n 次释放该锁后,其他线程能获取到锁。实现此功能,理应考虑使用计数

ReentrantLock 通过组合自定义同步器来实现锁的获取与释放,以非公平锁实现为例,获取同步状态的代码如下所示,主要是增加了再次获取同步状态的处理逻辑

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {  if (compareAndSetState(0, acquires)) {   setExclusiveOwnerThread(current);   return true;  } } // 判断当前线程是否为获取锁的线程 else if (current == getExclusiveOwnerThread()) {  // 将同步值进行增加,并返回 true  int nextc = c + acquires;  if (nextc < 0)   throw new Error("Maximum lock count exceeded");  setState(nextc);  return true; } return false;}

考虑到成功获取锁的线程再次获取锁,只是增加同步状态值,这也就要求 ReentrantLock 在释放同步状态时减少同步状态值,该方法代码如下:

protected final boolean tryRelease(int releases) { // 减少状态值 int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread())  throw new IllegalMonitorStateException(); boolean free = false; // 当同步状态为0,将占有线程设为null,并返回true,表示释放成功 if (c == 0) {  free = true;  setExclusiveOwnerThread(null); } setState(c); return free;}

2. 公平与非公平获取锁的区别

如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也即 FIFO。回顾上一节,非公平锁只要 CAS 设置同步状态成功,即表示当前线程获取了锁,而公平锁则不同,代码如下:

protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {    if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {   setExclusiveOwnerThread(current);   return true;  } } else if (current == getExclusiveOwnerThread()) {  int nextc = c + acquires;  if (nextc < 0)   throw new Error("Maximum lock count exceeded");  setState(nextc);  return true; } return false;}

读写锁

之前提到的锁基本都是排它锁,同一时刻只允许一个线程访问,而读写锁在同一时刻可以允许多个线程访问,但在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排它锁有了很大提升

1. 接口示例

下面通过缓存示例说明读写锁的使用方式

public class Cache { static Map<String, Object> map = new HashMap<>(); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock r = rwl.readLock(); static Lock w = rwl.writeLock();  public static Object get(String key) {  r.lock();  try {   return map.get(key);  } finally {   r.unlock();  } }  public static Object put(String key, Object value) {  w.lock();  try {   return map.put(key, value);  } finally {   w.unlock();  } }  public static void clear() {  w.lock();  try {   map.clear();  } finally {   w.unlock();  } }}

2. 读写状态的设计

读写锁同样依赖自定义同步器来实现功能,而读写状态就是其同步器状态。读写锁的自定义同步器需要在同步状态(一个整型变量)上维护多个读线程和一个写线程的状态,为此需要读写锁将变量切分成两部分,高 16 位表示读,低 16 位表示写

Java 重入锁和读写锁怎么使用

上图表示一个线程已经获取了写锁,且重进入了两次,同时也连续两次获取了读锁。通过位运算可以迅速确定读和写各自的状态,假设当前同步状态值为 S,则:

  • 写状态等于 S & 0x0000FFFF(将高 16 位全部抹去)

  • 读状态等于 S >>> 16(无符号右移 16 位)

  • 当写状态增加 1 时,等于 S + 1

  • 当读状态增加 1 时,等于 S + (1<<6),也就是 S + 0x00010000

根据状态的划分能得出一个结论:S 不等于 0 时,当写状态(S & 0x0000FFFF)等于 0 时,则读状态(S >>> 16)大于 0,即读锁已被获取

3. 写锁的获取与释放

写锁是一个支持重进入的排它锁。如果当前线程已经获取了写锁,则增加写状态。如果当前线程在获取写锁时,读锁已被获取,或者该线程不是获取写锁的线程,则当前线程进入等待状态,获取写锁的代码如下:

protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread(); int c = getState(); // exclusiveCount 方法会用 c & 0x0000FFFF,即得出写状态个数 int w = exclusiveCount(c); if (c != 0) {  // 根据上面提到的推论,c 不等于 0,而 w 等于 0,证明存在读锁  // 当前线程也不是获取了写锁的线程  if (w == 0 || current != getExclusiveOwnerThread())   return false;  if (w + exclusiveCount(acquires) > MAX_COUNT)   throw new Error("Maximum lock count exceeded");  setState(c + acquires);  return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires))  return false; setExclusiveOwnerThread(current); return true;}

写锁的每次释放均会减少写状态,当写状态为 0 时表示写锁已被释放,从而等待的读写线程能够继续访问读写锁,同时前次写线程的修改对后续读写线程可见

4. 读锁的获取与释放

读锁是一个支持重进入的共享锁,它能被多个线程同时获取,在没有其他写线程访问时,读锁总能被成功获取,这里对获取读锁的代码做了简化:

protected final int tryAcquireShared(int unused) { for(;;) {  int c = getState();  int nextc = c + (1<<16);  if(nextc < c) {   throw new Error("Maximum lock count exceeded");  }  // 如果其他线程已经获取写锁,则读取获取失败  if(exclusiveCount(c) != 0 && owner != Thread.currentThread()) {   return -1;  }  if(compareAndSetState(c, nextc)) {   return 1;  } }}

读锁的每次释放均减少读状态,减少的值是 1<<16

5. 锁降级

锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住写锁,再获取读锁,随后释放写锁的过程

public void processData() { readLock.lock(); if(!update) {  // 必须先释放读锁  readLock.unlock();  // 锁降级从写锁获取到开始  writeLock.lock();  try { if(!update) {    // 准备数据的流程(略)    update = true;   }   readLock.lock();  } finally {   writeLock.unlock();  } } try {  // 使用数据的流程(略) } finally {  readLock.unlock(); }}

上例中,当数据发生变更,则 update(使用 volatile 修饰)被设置为 false,此时所有访问 processData 方法的线程都能感知到变化,但只有一个线程能获取到写锁,其余线程会被阻塞在写锁的 lock 方法上。当前线程获取写锁完成数据准备之后,再次获取读锁,随后释放写锁,完成锁降级

到此,关于“Java 重入锁和读写锁怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java 重入锁和读写锁怎么使用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java 重入锁和读写锁怎么使用

这篇文章主要介绍“Java 重入锁和读写锁怎么使用”,在日常操作中,相信很多人在Java 重入锁和读写锁怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java 重入锁和读写锁怎么使用”的疑惑有所帮助!
2023-06-08

Java读写锁ReentrantReadWriteLock怎么使用

这篇文章主要介绍“Java读写锁ReentrantReadWriteLock怎么使用”,在日常操作中,相信很多人在Java读写锁ReentrantReadWriteLock怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法
2023-06-02

使用python实现可重入的公平读写锁

在本项目中,读写锁主要应用于多线程服务器场景下的日志文件的读写,以及缓存的获取和更新。 多线程编程的准标准库posix pthread库拥有rwlock, 而python2.7自带的threading库没有读写锁,只有可重入锁RLock,
2023-01-31

java读写锁怎么使用及优点是什么

这篇文章主要介绍了java读写锁怎么使用及优点是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java读写锁怎么使用及优点是什么文章都会有所收获,下面我们一起来看看吧。前言:读写锁(Readers-Writ
2023-06-30

java读写锁的使用方法是什么

在Java中,读写锁是一种特殊的锁,允许多个线程同时读取共享资源,但只允许一个线程进行写操作。读写锁通过ReadWriteLock接口实现,其中最常用的实现类是ReentrantReadWriteLock。下面是Java读写锁的使用方法:
java读写锁的使用方法是什么
2024-04-03

C++的std::shared_mutex读写锁怎么使用

这篇“C++的std::shared_mutex读写锁怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C++的std:
2023-06-29

GO语言协程互斥锁Mutex和读写锁RWMutex怎么用

本文小编为大家详细介绍“GO语言协程互斥锁Mutex和读写锁RWMutex怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“GO语言协程互斥锁Mutex和读写锁RWMutex怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一
2023-06-30

GoLang中的互斥锁Mutex和读写锁RWMutex使用教程

RWMutex是一个读/写互斥锁,在某一时刻只能由任意数量的reader持有或者一个writer持有。也就是说,要么放行任意数量的reader,多个reader可以并行读;要么放行一个writer,多个writer需要串行写
2023-01-09

java读写锁的使用场景有哪些

当多个线程需要读取共享数据,但只有少数线程需要写入数据时,使用读写锁可以提高并发性能。当对共享数据的读操作比写操作频繁时,使用读写锁可以减少写操作的竞争,提高系统性能。当需要保证对数据的读写操作是线程安全的时候,可以使用读写锁来控制并发访问
java读写锁的使用场景有哪些
2024-04-03

一文了解Java读写锁ReentrantReadWriteLock的使用

ReentrantReadWriteLock称为读写锁,它提供一个读锁,支持多个线程共享同一把锁。这篇文章主要讲解一下ReentrantReadWriteLock的使用和应用场景,感兴趣的可以了解一下
2022-11-13

Java锁怎么使用

这篇文章主要介绍了Java锁怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java锁怎么使用文章都会有所收获,下面我们一起来看看吧。乐观锁和悲观锁悲观锁悲观锁对应于生活中悲观的人,悲观的人总是想着事情往
2023-07-02

mysql行锁和表锁怎么使用

MySQL中的行锁和表锁是用来控制并发访问数据库的机制,可以防止多个用户同时修改同一行或同一表的数据,保证数据的一致性和完整性。1. 行锁:行锁是对数据表中的某一行进行锁定,只有锁定的行才能被修改。行锁可以通过以下方式来使用:- 在需要锁定
2023-09-11

怎么实现Java可重入分布式锁

本篇内容主要讲解“怎么实现Java可重入分布式锁”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么实现Java可重入分布式锁”吧!可重入说到可重入锁,首先我们来看看一段来自 wiki 上可重入的
2023-06-16

java可重入锁的使用场景有哪些

可重入锁是指同一个线程可以多次获得同一把锁,在释放锁之前需要释放相同次数的锁。可重入锁的使用场景包括:1. 递归函数:当一个递归函数需要获取锁来保护共享资源时,可重入锁可以允许递归函数多次获取同一把锁。2. 锁的嵌套:当一个方法A获得了锁之
2023-09-11

Java多线程中读写锁分离设计模式怎么用

小编给大家分享一下Java多线程中读写锁分离设计模式怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!主要完成任务:1.read read 并行化2.read
2023-06-25

Java重写锁的设计结构和细节是什么

这篇文章主要介绍“Java重写锁的设计结构和细节是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java重写锁的设计结构和细节是什么”文章能帮助大家解决问题。引导语有的面试官喜欢让同学在说完锁的
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录