Java 读写锁源码分析

 更新时间:2021年01月30日 10:06:29   作者:消灭知识盲区  
这篇文章主要介绍了Java 读写锁的相关资料,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下

前言

在实际项目中,比如我们有一个共享资源文件,我们程序会会同时并发的去读、写这个共享资源文件,那怎么能保证在高并发场景下安全、高效读写呢?OK,看了下文便知

提示:以下是本篇文章正文内容,案例仅供参考

一、技术介绍

1.ReentranReadWriteLock是什么?

ReadWriteLock提供了readLock和writeLock两种锁的操作机制,一个是读锁,一个是写锁,而它的实现类就是ReentranReadWriteLock

读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的(排他的)。这意味着每次只能有一个线程写,但可以多个线程并发地读。

理论上,读写锁比互斥锁允许对于共享数据更大程度的并发。与互斥锁相比,读写锁是否能够提高性能取决于读写数据的频率、读取和写入操作的持续时间、以及读线程和写线程之间的竞争。

二、源码分析

1.ReadLock

直接看ReadLock源码

可以看到ReadLock的lock操作实际实现对应Sync的 tryAcquireShared 方法,我们点进去看看

  1. 如果另一个线程持有写锁,则失败。
  2. readerShouldBlock():读锁是否需要等待(公平锁原则); r < MAX_COUNT:持有线程小于最大数 ;compareAndSetState(c, c + SHARED_UNIT):设置读取锁状态;整体意思就是读线程是否应该被阻塞、并且小于最大值、并且比较设置成功
  3. else判断里读锁数量不为0并且不为当前线程,获取计数器并且+1
  4. 最后的这行方法执行是由于线程显然不符合条件,或者CAS失败或计数饱和,使用完整版本的获取,这里面的方法可处理CAS miss 和tryAcquireShared中未处理的可重入读

2.WriteLock

再接着看WriteLock源码

exclusiveCount方法的作用是将同步变量与0xFFFF做&运算,计算结果就是写锁的数量,因此w的值的含义就是写锁的数量 如果c不为0就表示锁被占用了,占用的是写锁还是读锁呢?这个时候就需要根据w的值来判断了。 如果c等于0就表示此时锁还没有被任何线程占用,那就让线程直接去尝试获取锁.

如果w为0,说明写锁数量为0,而此时又因为c不等于0,说明锁被占用,但是不是写锁,那么此时锁的状态一定是读锁,既然是读锁状态,那么写锁此时来获取锁时,就肯定失败,因此当w等于0时,tryAcquire方法返回false。

如果w不为0,说明此时锁的状态时写锁,接着进行current != getExclusiveOwnerThread()判断,判断持有锁的线程是否是当前线程

如果不是当前线程,那么tryAcquire()返回false;如果是当前线程,那么就进行后面的逻辑。为什么是当前线程持有锁,就还能执行后面的逻辑呢?那是因为读写锁是可重入的。

三、单元测试

OK,不废话,接下来上简单直接的单元测试

package com.hyh.core.test;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 可重入读写锁 TEST
 *
 * @Author: heyuhua
 * @Date: 2021/1/28 14:14
 */
public class ReentranReadWriteLockTest {

  ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
  Lock readLock = reentrantReadWriteLock.readLock();
  Lock writeLock = reentrantReadWriteLock.writeLock();


  @Test
  public void test() throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 5; i++) {
      threadPool.execute(() -> {
        readLock();
      });
      threadPool.execute(() -> {
        writeLock();
      });
    }
    threadPool.shutdown();
    TimeUnit.SECONDS.sleep(10);
  }

  private void readLock() {
    readLock.lock();
    try {
      System.out.println(Thread.currentThread().getName() + "-获取读锁...");
      TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      System.out.println(Thread.currentThread().getName() + "-释放读锁...");
      readLock.unlock();
    }
  }

  private void writeLock() {
    writeLock.lock();
    try {
      System.out.println(Thread.currentThread().getName() + "-获取写锁...");
      TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      System.out.println(Thread.currentThread().getName() + "-释放写锁...");
      writeLock.unlock();
    }
  }


}

这里模拟5个读和5个写的并发情况,马上看下执行结果

可以看到多个线程都可以同时获得读锁,而写锁必须等读锁释放了后才能获取,同样的读锁必须等写锁释放了后才能获取

总结

读写锁总结一句话:读读共享、写写互斥、读写互斥。

读写锁的适用场合:相比于 ReentrantLock 适用于一般场合,ReadWriteLock 适用读多写少的场景,合理使用可以进一步提高并发

作者寄语

是不是感觉很简单?更多用法请点击下方查看源码,关注我带你揭秘更多高级用法

源码地址:点此查看源码

以上就是Java 读写锁源码分析的详细内容,更多关于Java 读写锁的资料请关注脚本之家其它相关文章!

相关文章

  • 使用Java实现接口拦截器来监控接口的执行情况

    使用Java实现接口拦截器来监控接口的执行情况

    在排查问题的时候,由于没有对接口的执行情况,以及入参进行监控,所以排查起问题就特别费劲,今天我们就一起来写一个接口的拦截器来监控接口的执行情况吧
    2024-01-01
  • mybatis-plus中的Enum用法实例

    mybatis-plus中的Enum用法实例

    本文主要介绍了mybatis-plus中的Enum用法实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 手把手教你如何利用SpringBoot实现审核功能

    手把手教你如何利用SpringBoot实现审核功能

    审核功能经过几个小时的奋战终于完成了,现在我就与广大网友分享我的成果,这篇文章主要给大家介绍了关于如何利用SpringBoot实现审核功能的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • 一文彻底搞懂Java日期时间类详解

    一文彻底搞懂Java日期时间类详解

    这篇文章主要给大家介绍了关于Java日期时间类的相关资料,Calendar类的功能要比Date类强大很多,可以方便的进行日期的计算,获取日期中的信息时考虑了时区等问题,需要的朋友可以参考下
    2023-10-10
  • Java基于注解实现的锁实例解析

    Java基于注解实现的锁实例解析

    这篇文章主要介绍了Java基于注解实现的锁实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • mybatis 为什么千万不要使用 where 1=1

    mybatis 为什么千万不要使用 where 1=1

    这篇文章主要介绍了mybatis 为什么千万不要使用 where 1=1,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • 深入理解Java的接口与抽象类

    深入理解Java的接口与抽象类

    本文主要介绍java 的接口和抽象类,对接口和抽象类进行介绍对比,深入理解,有需要的小伙伴可以参考下
    2016-07-07
  • Java数组声明、创建、初始化基础

    Java数组声明、创建、初始化基础

    本文讲述了Java数组的几个相关的方面,讲述了对Java数组的声明、创建和初始化,并给出其对应的代码
    2012-12-12
  • Spring中如何获取request的方法汇总及其线程安全性分析

    Spring中如何获取request的方法汇总及其线程安全性分析

    这篇文章主要给大家介绍了关于Spring中如何获取request的方法汇总及其线程安全性分析的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-04-04
  • Java中Singleton的3种实现方式详解

    Java中Singleton的3种实现方式详解

    这篇文章主要给大家介绍了关于Java中Singleton的3种实现方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02

最新评论