亮,有一计

文章 分类 评论
49 17 9

站点介绍

这是一个热爱和别人一起交流学习的程序员所建立的个人博客 欢迎大家交流讨论

ReentrantLock、ReentrantReadWriteLock、StampedLock解读

liang 2022-07-30 122 0条评论 java锁多线程知识juc多线程编码学习 读写锁邮戳锁

首页 / 正文
本站的文章若无特殊标明转载的话均为原创,有些文章在我的csdn个人站也有,两者基本上是同步的 博客地址

发布于2022-07-08

ReentrantLock

看上一篇的AQS源码深度解读

ReentrantReadWriteLock

它的含义和作用

『读写锁ReentrantReadWriteLock』并不是真正意义上的读写分离,它只允许读读共存,而读写和写写依然是互斥的,大多实际场景是“读/读”线程间并不存在互斥关系,只有"读/写"线程或"写/写"线程间的操作需要互斥的。因此引入ReentrantReadWriteLock。
一个ReentrantReadWriteLock同时只能存在一个写锁但是可以存在多个读锁,但不能同时存在写锁和读锁,只有在读多写少情境之下,读写锁才具有较高的性能体现.

代码示例

注意 读写锁有一个问题就是读线程没有完成读的时候 写线程是一直写不进去的,高并发下可能会有写线程处于锁饥饿的状态
static Map<Integer, String> map = new HashMap<>();

    static {
        map.put(1, "1");
        map.put(2, "2");
        map.put(3, "3");
        map.put(4, "4");
        map.put(5, "5");
        map.put(6, "6");
        map.put(7, "7");
        map.put(8, "8");
        map.put(9, "9");
        map.put(10, "10");
        map.put(0, "0");
    }

    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public static void write(Integer key, String value) {
        lock.writeLock().lock();
        try {
            map.put(key, value);
            System.out.println("写进去了值:" + map.get(key));
        } finally {
            lock.writeLock().unlock();
        }
    }

    public static void read(Integer key) throws InterruptedException {
        lock.readLock().lock();
        try {
            //TimeUnit.MILLISECONDS.sleep(4000);
            System.out.println("读取出来了值:" + map.get(key));
        } finally {
            lock.readLock().unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    read(finalI);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }, "read" + i).start();
        }

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                write(finalI, String.valueOf(finalI + 10));
            }, "read" + i).start();
        }
    }

读写锁的锁降级

在规定的操作步骤下,写锁是可以自动降级为读锁的,步骤如下
遵循获取写锁→再获取读锁→再释放写锁的次序,写锁能够降级成为读锁。
就是说一个线程占有了写锁,在不释放写锁的情况下,它还能占有读锁,这时候写锁就直接降级为读锁,此时其他的读锁也可以实时读取到数据.他的目的就是为了让当前的所有读线程感知到数据的变化,目的是保证数据可见性
代码示例:

public static void main(String[] args)
    {
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();


        writeLock.lock();
        System.out.println("-------正在写入");


        readLock.lock();
        System.out.println("-------正在读取");

        writeLock.unlock();





    }

读写锁总结

ReentrantReadWriteLock本质上是一种悲观锁,如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,ReadWriteLock读的过程中不允许写,只有等待线程都释放了读锁,当前线程才能获取写锁,也就是写入必须等待,这是一种悲观的读锁.
官方锁降级示例代码:
读写锁降级示例代码

  • 代码中声明了一个volatile类型的cacheValid变量,保证其可见性。
  • 首先获取读锁,如果cache不可用,则释放读锁,获取写锁,在更改数据之前,再检查一次cacheValid的值,然后修改数据,将cacheValid置为true,然后在释放写锁前获取读锁;此时,cache中数据可用,处理cache中数据,最后释放读锁。这个过程就是一个完整的锁降级的过程,目的是保证数据可见性。

如果违背锁降级的步骤
如果当前的线程C在修改完cache中的数据后,没有获取读锁而是直接释放了写锁,那么假设此时另一个线程D获取了写锁并修改了数据,那么C线程无法感知到数据已被修改,则数据出现错误。
如果遵循锁降级的步骤
线程C在释放写锁之前获取读锁,那么线程D在获取写锁时将被阻塞,直到线程C完成数据处理过程,释放读锁。这样可以保证返回的数据是这次更新的数据,该机制是专门为了缓存设计的。

StampedLock邮戳锁

定义解释

StampedLock是JDK1.8中新增的一个读写锁,也是对JDK1.5中的读写锁ReentrantReadWriteLock的优化,也称之为票据锁.
它内部有一个stamp(戳记,long类型),代表了锁的状态。当stamp返回零时,表示线程获取锁失败。并且,当释放锁或者转换锁的时候,都要传入最初获取的stamp值。

解决痛点

他是为了解锁读写锁会导致写线程锁饥饿的问题而推出来的,读写锁的写线程锁饥饿可以使用公平锁一定程度上的缓解,但是就牺牲掉了系统的吞吐量,所以StampedLock横空出世.

使用特点

  • 所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为零表示获取失败,其余都表示成功;
  • 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的Stamp一致;
  • StampedLock是不可重入的,危险(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)
  • StampedLock有三种访问模式

    1. Reading(读模式):功能和ReentrantReadWriteLock的读锁类似
    2. Writing(写模式):功能和ReentrantReadWriteLock的写锁类似
    3. Optimistic reading(乐观读模式):无锁机制,类似于数据库中的乐观锁,支持读写并发,很乐观认为读取时没人修改,假如被修改再实现升级为悲观读模式

    代码示例:

    static int number = 37;
      static StampedLock stampedLock = new StampedLock();
    
      public void write()
      {
          long stamp = stampedLock.writeLock();
          System.out.println(Thread.currentThread().getName()+"\t"+"=====写线程准备修改");
          try
          {
              number = number + 13;
          }catch (Exception e){
              e.printStackTrace();
          }finally {
              stampedLock.unlockWrite(stamp);
          }
          System.out.println(Thread.currentThread().getName()+"\t"+"=====写线程结束修改");
      }
    
      //悲观读
      public void read()
      {
          long stamp = stampedLock.readLock();
          System.out.println(Thread.currentThread().getName()+"\t come in readlock block,4 seconds continue...");
          //暂停4秒钟线程
          for (int i = 0; i <4 ; i++) {
              try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
              System.out.println(Thread.currentThread().getName()+"\t 正在读取中......");
          }
          try
          {
              int result = number;
              System.out.println(Thread.currentThread().getName()+"\t"+" 获得成员变量值result:" + result);
              System.out.println("写线程没有修改值,因为 stampedLock.readLock()读的时候,不可以写,读写互斥");
          }catch (Exception e){
              e.printStackTrace();
          }finally {
              stampedLock.unlockRead(stamp);
          }
      }
    
      //乐观读
      public void tryOptimisticRead()
      {
          long stamp = stampedLock.tryOptimisticRead();
          //先把数据取得一次
          int result = number;
          //间隔4秒钟,我们很乐观的认为没有其他线程修改过number值,愿望美好,实际情况靠判断。
          System.out.println("4秒前stampedLock.validate值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
          for (int i = 1; i <=4 ; i++) {
              try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
              System.out.println(Thread.currentThread().getName()+"\t 正在读取中......"+i+
                      "秒后stampedLock.validate值(true无修改,false有修改)"+"\t"
                      +stampedLock.validate(stamp));
          }
          if(!stampedLock.validate(stamp)) {
              System.out.println("有人动过--------存在写操作!");
              //有人动过了,需要从乐观读切换到普通读的模式。
              stamp = stampedLock.readLock();
              try {
                  System.out.println("从乐观读 升级为 悲观读并重新获取数据");
                  //重新获取数据
                  result = number;
                  System.out.println("重新悲观读锁通过获取到的成员变量值result:" + result);
              }catch (Exception e){
                  e.printStackTrace();
              }finally {
                  stampedLock.unlockRead(stamp);
              }
          }
          System.out.println(Thread.currentThread().getName()+"\t finally value: "+result);
      }
    
      public static void main(String[] args)
      {
          StampedLockDemo resource = new StampedLockDemo();
          
    
          //3 乐观读,失败,重新转为悲观读,重读数据一次
          new Thread(() -> {
              //乐观读
              resource.tryOptimisticRead();
          },"readThread").start();
    
          //2秒钟乐观读取resource.tryOptimisticRead()失败
          try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
    
    
          new Thread(() -> {
              resource.write();
          },"writeThread").start();
    
      }
    StampedLock读的过程中也允许获取写锁介入,但是乐观读取到的票据很有可能已经被写锁改动过了,我们需要校验一下,如果被改动过,在转换为悲观读的模式再去重新读取改动过后的数据

    邮戳锁的缺点

  • StampedLock 不支持重入,没有Re开头
  • StampedLock 的悲观读锁和写锁都不支持条件变量(Condition),这个也需要注意。
  • 使用 StampedLock一定不要调用中断操作,即不要调用interrupt() 方法

评论(0)

最新评论

  • Hello Word !

    我发现我的评论功能,加载不了头像!朋友圈和相册也加载不了,音乐播放器也不显示,求解!

  • il: Clark11370

    Margenemh 357 fotballdrakter til bedriftslag Coreyc – Coin Media – журнал о заработке в интернете Francesco CasieDalu maglia argentina AbbieLand JessRoman Website Darrellie PaulinaRu Profile of ChristieRy SidneyMgw ToneyKrog Liverpool Tøj - Blog Profile - iotaJots JaimieChe TriciaBri Wolearn: GregoryEmelia: Magliette Calcio Poco Prezzo SheltonTh WayneHone 438 Chile Tröja Barn Leopol – Profile – Primescool Forum CharlineC VitoHartf norge bortedrakt to receive adulation, always insists the result is most important and will therefore be thoroughly disheartened by this display. As is often the case in friendly matches, there was a lack FedericoW صفحه اصلی – Maglia Porto KurtBrise of urgency for large periods during this match, though Wales did link play promisingly in attack through Wilson and Brooks. Italy will be among the nations in pot one for the Euro 2020 qualifying draw in Dublin on 2 December having drawn 0-0 against Portugal in their previous match to ensure a second-place finish in Nations League Group 3A. TanjaFitz Elizabeth manchester city tröjor KenPoulin CarenWhar Maglia Juventus Bambino EmeliaBow Stephaine liverpool drakt DarlaFulm FerneSuvg barcelona FC drakt Meaganold EmilyEise arsenal drakt KourtneyS Kellyejoh inter trøje JamaalWor Loisniyet psg drakt MaricelaS CecileSwa lavora come produttore esecutivo della serie CharissaC ChastityF Home - Liverpool Drakt EmilGmvut CasieDalu maglia argentina AbbieLand UrsulaLof rashford numero maglia KareemCap SSNGilber arsenal drakt 2022 MelodeePe

  • arsenal trøjer

    BrittnyFo atletico madrid tröja ClaritaBu ZJRBridge magliette psg JosephDur Valentina real madrid drakt MarionTol NevaLangt manchester united drakt BraydenBo RodolfoCa Real Madrid Tröja KelliefjL BettinaMi Notenbrood op top niveau MillardMh RichardMo arsenal drakt Bradleyjo TanyaNuge Spanien landsholdstrøje AprilBuck JoannaShe Billige Polen Fotballdrakter NiklasWes Maryellen terza maglia milan PrincessN AnhGiffen real madrid tröja FredricAu JoannaShe Billige Polen Fotballdrakter NiklasWes EssieOate juventus matchtröja PreciousF EdnaTippe sabinahogan447 EffieOcon MichaelaH liverpool tröja barn KayleighM JonnieHoc nuova maglia roma 2021/22 CortezSha YDZClaire ac milan drakt KathleneH CedricBac manchester united drakt Peterysjl HaicoQcmw Maglia Brasile Femmina ThereseJu DoreenMes napoli trøje JadaLanni

  • maglie hellas verona 2022

    Your style is very unique in comparison to other folks I have read stuff from. Many thanks for posting when you have the opportunity, Guess I'll just bookmark this blog. maglie hellas verona 2022 IlanaGxrn frankrike tröja JaneenZtt

  • inter milan tröja

    I truly love your site.. Pleasant colors & theme. Did you make this website yourself? Please reply back as I'm hoping to create my own website and would like to learn where you got this from or just what the theme is named. Cheers! inter milan tröja LilianaCz Frankrike Tröja Barn NganRjpfi

日历

2022年12月

    123
45678910
11121314151617
18192021222324
25262728293031

友情链接

文章目录

推荐关键字:

站点公告
本站的文章若无特殊标明转载的话均为原创,有些文章在我的csdn个人站也有,两者基本上是同步的 博客地址
点击小铃铛关闭