java并发编程中ReentrantLock可重入读写锁

作者:字母哥博客 时间:2021-12-10 16:06:17 

一、ReentrantLock可重入锁

可重入锁ReentrantLock 是一个互斥锁,即同一时间只有一个线程能够获取锁定资源,执行锁定范围内的代码。这一点与synchronized 关键字十分相似。其基本用法代码如下:

Lock lock = new ReentrantLock();  //实例化锁
//lock.lock(); //上锁
boolean locked = lock.tryLock();  //尝试上锁
if(locked){
 try {
   //被锁定的同步代码块,同时只能被一个线程执行
 }finally {
   lock.unlock(); //放在finally代码块中,保证锁一定会被释放
 }
}

通过lock函数获取锁,通过unlock函数释放锁。非常重要的是,需要把需要同步执行的代码放入 try/finally 代码块中,并在finally中将锁释放。ReentrantLock是可重入锁,即:(lock/unlok)动作里面可以嵌套(lock/unlock),针对同一个锁可以多次嵌套使用,不会产生死锁。但是lock函数与unlock函数在代码中必须成对出现,否则会出现死锁

二、ReentrantReadWriteLock读写锁

ReentrantReadWriteLock类为读写锁实现类,针对某一个对象或可变变量,只要没有线程在修改它,这个对象或可变变量就可以同时被多个线程读取。ReentrantReadWriteLock将锁分为读锁和写锁,只要没有线程持有写锁的情况下,读锁可以由多个线程同时持有。

  • 读锁-如果没有线程获取或请求写锁,那么多个线程可以获取读锁

  • 写锁-如果没有线程在读或写,那么只有一个线程可以获得写锁

简单的说就是ReentrantReadWriteLock可以保证最多同时有一个线程在写数据,或者可以同时有多个线程读数据。因此使用ReentrantReadWriteLock,在读操作比写操作更频繁的情况下,可以提高程序的性能和吞吐量。

下面我们用一个简单的例子,来解读一下如何应用读写锁。

public class TestReadWriteLock {
 //可以同时执行3个线程任务的线程池
 ExecutorService executor = Executors.newFixedThreadPool(3);
 //读写目标,写线程放入数据到map,读线程从map读取数据
 Map<String, String> map = new HashMap<>();
 //读写锁操作对象
 ReadWriteLock lock = new ReentrantReadWriteLock();

//写操作函数
 public void write(){
   executor.submit(() -> { //线程池提交写操作任务
     lock.writeLock().lock(); //加写锁
     try {
       map.put("key", "val");  //写数据操作
       Thread.sleep(2000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     } finally {
       lock.writeLock().unlock(); //释放写锁
     }
   });
 }

//读操作函数
 public void read(){
   lock.readLock().lock(); //加读锁
   System.out.println(Thread.currentThread().getName() + "加读锁");
   try {
     System.out.println(map.get("key")); //读数据操作
   } finally {
     lock.readLock().unlock(); //释放读锁
     System.out.println(Thread.currentThread().getName() + "释放读锁");
   }
 }

}

三、读锁之间不互斥

我们写一个测试方法,通过打印输出来理解读写锁控制代码的执行顺序。

//测试
 public static void main(String[] args) {
   TestReadWriteLock test = new TestReadWriteLock();
   test.write();  //提交一次写操作任务,写一条数据
   Runnable readTask = test::read;  //线程方法read,实现线程Runnable接口的简便写法
   test.executor.submit(readTask);  //读1次(新读线程)
   test.executor.submit(readTask);  //读2次 (新读线程)
   test.executor.shutdown();
 }

执行上面的代码,可能会出现下面的输出

pool-1-thread-2加读锁
pool-1-thread-3加读锁
val
val
pool-1-thread-3释放读锁
pool-1-thread-2释放读锁

在pool-1-thread-2没有释放读锁情况下,pool-1-thread-3可以再次加读锁,并且都正确的读取到数据val。说明读锁之间是不互斥的。但是,在进行读操作(读锁生效)的时候,写操作是无法进行的(无法获取写锁),所以ReentrantReadWriteLock不支持同时加读锁和写锁。 这个结论我可以负责任告诉大家,这里我就不做验证了!

来源:https://www.cnblogs.com/zimug/p/16272252.html

标签:java,ReentrantLock,可重入读写锁
0
投稿

猜你喜欢

  • Spring 4.0新功能:@Conditional注解详细介绍

    2022-01-19 06:37:35
  • JAVA抽象类,接口,内部类详解

    2023-11-09 16:37:25
  • C#用NPOI导出导入Excel帮助类

    2022-09-27 00:23:43
  • Java对List进行排序的两种实现方法

    2022-10-31 13:40:06
  • Java中Range函数的简单介绍

    2023-10-18 05:33:35
  • JDK1.8新特性之方法引用 ::和Optional详解

    2021-09-13 08:47:33
  • SpringBoot Bean被加载时进行控制

    2022-10-01 14:06:53
  • Java中防止数据重复提交超简单的6种方法

    2022-03-18 11:03:22
  • 前端如何调用后端接口进行数据交互详解(axios和SpringBoot)

    2023-10-17 02:48:43
  • SpringMVC实现表单验证功能详解

    2023-09-24 09:07:28
  • SpringCloud @RefreshScope刷新机制浅析

    2022-12-13 11:38:15
  • Java KindEditor粘贴图片自动上传到服务器功能实现

    2023-08-07 01:42:33
  • Java中反射动态 代理接口的详解及实例

    2023-11-28 05:29:36
  • Java中常见的陷阱题及答案

    2021-08-10 16:32:11
  • Java动态显示文件上传进度实现代码

    2022-09-09 20:18:17
  • 详解Spring Boot加载properties和yml配置文件

    2023-11-24 07:14:09
  • JAVA过滤标签实现将html内容转换为文本的方法示例

    2022-10-16 21:02:40
  • 关于springboot集成swagger及knife4j的增强问题

    2023-11-29 00:43:50
  • Java中常见的5种WEB服务器介绍

    2022-08-02 08:45:50
  • C#计算字符串相似性的方法

    2022-08-18 11:32:15
  • asp之家 软件编程 m.aspxhome.com