java并发编程StampedLock高性能读写锁详解

目录
  • 一、读写锁
  • 二、悲观读锁
  • 三、乐观读

一、读写锁

在我的《java并发编程》上一篇文章中为大家介绍了《ReentrantLock读写锁》,ReentrantReadWriteLock可以保证最多同时有一个线程在写数据,或者可以同时有多个线程读数据,但读写不能同时进行。

比如你正在做的是日志,有一个线程正在做写操作,但是在写日志的时候你可能需要把日志集中转移到集中管理日志服务,但是此时读线程不能读数据(因为无法获取读锁)。面对这个需求,ReentrantReadWriteLock显然不是我们的解决方案,我们希望:最多一个线程在进行写操作(加写锁),但是同时允许多个线程进行读操作(加读锁),解决方案是StampedLock。

二、悲观读锁

StampedLock 同样可以实现写锁和读锁的功能,Stamped在英文中有印章的含义,对于StampedLock大家可以这么理解,使用一个印章加锁,必须使用该印章解锁。

public class TestStampedLock {
    Map<String,String> map = new HashMap<>();
    //锁对象
    private StampedLock lock = new StampedLock();
    //写操作函数
    public void put(String key, String value){
        long stamp = lock.writeLock(); //加写锁
        try {
            map.put(key, value); //写操作
        } finally {
            lock.unlockWrite(stamp);  //释放写锁
        }
    }
    public String get(String key) {
        long stamp = lock.readLock(); //加读锁
        try {
            return map.get(key); //读操作
        } finally {
            lock.unlockRead(stamp); //释放读锁
        }
    }
}

上文中的读锁readLock,在StampedLock模式中被称为悲观读锁,之所以叫做悲观读锁是和StampedLock支持的另一种模式“乐观读”相对应的。

写锁、悲观读锁的语义和 ReadWriteLock 的写锁、读锁的语义基本是一致的,允许多个线程同时获取悲观读锁,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。

多线程环境下,写操作的同时不能读。所以到这里为止,StampedLock与ReadWriteLock并没有很大的区别。

三、乐观读

需要注意的是,这里我写的是乐观读,而不是乐观读锁,因为乐观读是不加锁的。通过tryOptimisticRead()函数获取一个stamp,这里的tryOptimisticRead() 就是乐观读,乐观读因为没有加锁,所以读取数据的性能会更高一点。即:已经有写操作线程加锁的同时,仍然允许读操作线程继续进行。

如果你的读写操作有比较强的时间点数据一致性要求,即:同一个时间点读操作读到的数据,一定与该时间点写操作保持数据一致性。那么,你就需要进行validate校验,stamp此时可以理解为一个版本号,如果写操作版本为2,读操作版本为1,说明你读到的数据不是最新的。你需要去读取最新版本的数据(版本号为2),所以需要升级为悲观读锁,代码如下:

public String readWithOptimisticLock(String key) {
    long stamp = lock.tryOptimisticRead(); //乐观读
    String value = map.get(key); //读取数据
    if(!lock.validate(stamp)) {  //校验数据是否是最新版本
        stamp = lock.readLock();  //如果不是,升级为悲观读锁
        try {
            return map.get(key);
        } finally {
            lock.unlock(stamp);
        }
    }
    return value;
}

以上就是java并发编程StampedLock高性能读写锁详解的详细内容,更多关于java并发StampedLock读写锁的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java并发之搞懂读写锁

    目录 ReentrantReadWriteLock 小结 StampedLock 小结 总结 ReentrantReadWriteLock 我们来探讨一下java.concurrent.util包下的另一个锁,叫做ReentrantReadWriteLock,也叫读写锁. 实际项目中常常有这样一种场景: 比如有一个共享资源叫做Some Data,多个线程去操作Some Data,这个操作有读操作也有写操作,并且是读多写少的,那么在没有写操作的时候,多个线程去读Some Data是不会有线程安全问

  • Java并发编程之ReadWriteLock读写锁的操作方法

    1.ReadWriteLock介绍 为什么我们有了Lock,还要用ReadWriteLock呢.我们对共享资源加锁之后,所有的线程都将会等待.Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其实是不用加锁的.当然读写同时存在的情况也会有. 比如我们数据库常用操作有增删改查,增删改都是写操作,写操作必须加锁,而读操作可以共享.不是所有的操作都需要加锁. 为了进一步提高复用性和粒度,写操作独占,读操作共享,不加锁. ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁.读锁可以在

  • Java并发编程之StampedLock锁介绍

    StampedLock: StampedLock是并发包里面JDK8版本新增的一个锁,该锁提供了三种模式的读写控制,当调用获取锁的系列函数时,会返回一个long 型的变量,我们称之为戳记(stamp),这个戳记代表了锁的状态.其中try 系列获取锁的函数,当获取锁失败后会返回为0的stamp值.当调用释放锁和转换锁的方法时需要传入获取锁时返回的stamp值. StampedLock提供的三种读写模式的锁分别如下: 写锁witeLock: 是一个排它锁或者独占锁,某时只有一个线程可以获取该锁,当一

  • Java并发编程之重入锁与读写锁

    重入锁 重入锁,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁.重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,该特性的实现需要解决以下两个问题. 1.线程再次获取锁.锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取. 2.锁的最终释放.线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁.锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经成功释放

  • java并发编程StampedLock高性能读写锁详解

    目录 一.读写锁 二.悲观读锁 三.乐观读 一.读写锁 在我的<java并发编程>上一篇文章中为大家介绍了<ReentrantLock读写锁>,ReentrantReadWriteLock可以保证最多同时有一个线程在写数据,或者可以同时有多个线程读数据,但读写不能同时进行. 比如你正在做的是日志,有一个线程正在做写操作,但是在写日志的时候你可能需要把日志集中转移到集中管理日志服务,但是此时读线程不能读数据(因为无法获取读锁).面对这个需求,ReentrantReadWriteLoc

  • Java并发编程之阻塞队列深入详解

    目录 1. 什么是阻塞队列 2. 阻塞队列的代码使用 3. 生产者消费者模型 (1)应用一:解耦合 (2)应用二:削峰填谷 (3)相关代码 4.阻塞队列和生产者消费者模型功能的实现 1. 什么是阻塞队列 阻塞队列是一种特殊的队列,和数据结构中普通的队列一样,也遵守先进先出的原则同时,阻塞队列是一种能保证线程安全的数据结构,并且具有以下两种特性:当队列满的时候,继续向队列中插入元素就会让队列阻塞,直到有其他线程从队列中取走元素:当队列为空的时候,继续出队列也会让队列阻塞,直到有其他线程往队列中插入

  • Java并发编程(CyclicBarrier)实例详解

    Java并发编程(CyclicBarrier)实例详解 前言: 使用JAVA编写并发程序的时候,我们需要仔细去思考一下并发流程的控制,如何让各个线程之间协作完成某项工作.有时候,我们启动N个线程去做一件事情,只有当这N个线程都达到某一个临界点的时候,我们才能继续下面的工作,就是说如果这N个线程中的某一个线程先到达预先定义好的临界点,它必须等待其他N-1线程也到达这个临界点,接下来的工作才能继续,只要这N个线程中有1个线程没有到达所谓的临界点,其他线程就算抢先到达了临界点,也只能等待,只有所有这N

  • java并发编程专题(五)----详解(JUC)ReentrantLock

    上一节我们了解了Lock接口的一些简单的说明,知道Lock锁的常用形式,那么这节我们正式开始进入JUC锁(java.util.concurrent包下的锁,简称JUC锁).下面我们来看一下Lock最常用的实现类ReentrantLock. 1.ReentrantLock简介 由单词意思我们可以知道这是可重入的意思.那么可重入对于锁而言到底意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放.这模仿了 sy

  • java并发编程专题(三)----详解线程的同步

    有兴趣的朋友可以回顾一下前两篇 java并发编程专题(一)----线程基础知识 java并发编程专题(二)----如何创建并运行java线程 在现实开发中,我们或多或少的都经历过这样的情景:某一个变量被多个用户并发式的访问并修改,如何保证该变量在并发过程中对每一个用户的正确性呢?今天我们来聊聊线程同步的概念. 一般来说,程序并行化是为了获得更高的执行效率,但前提是,高效率不能以牺牲正确性为代价.如果程序并行化后, 连基本的执行结果的正确性都无法保证, 那么并行程序本身也就没有任何意义了.因此,

  • JAVA并发编程有界缓存的实现详解

    JAVA并发编程有界缓存的实现 1.有界缓存的基类 package cn.xf.cp.ch14; /** * *功能:有界缓存实现基类 *时间:下午2:20:00 *文件:BaseBoundedBuffer.java *@author Administrator * * @param <V> */ public class BaseBoundedBuffer<V> { private final V[] buf; private int tail; private int head

  • Java并发编程之常用的辅助类详解

    1.CountDownLatch 1.2.示例:班长锁门问题 问题描述:假如有7个同学晚上上自习,钥匙在班长手上,并且要负责锁门.班长必须要等所有人都走光了,班长才能关灯锁门.这6个同学的顺序是无序的,不知道它们是何时离开.6个同学各上各的自习,中间没有交互.假如说6个学生是普通线程,班长是主线程,如何让主线程要等一堆线程运行完了,主线程才能运行完成呢. public class CountDownLatchDemo { public static void main(String[] args

  • Java并发编程之阻塞队列(BlockingQueue)详解

    目录 队列 阻塞队列 ArrayBlockingQueue 重要属性 构造方法 添加元素 add(e) offer(e) put(e) offer(e,time,unit) 移除元素 take() dequeue() LinkedBlockingQueue 重要属性 构造方法 添加元素 offer(e) put(e) 移除元素 poll() take() 对比 总结 大家好,我是小黑,一个在互联网苟且偷生的农民工. 队列 学过数据结构的同学应该都知道,队列是数据结构中一种特殊的线性表结构,和平时

  • Java并发编程之ConcurrentLinkedQueue源码详解

    一.ConcurrentLinkedQueue介绍 并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式: 方式1:加锁,这种实现方式就是我们常说的阻塞队列. 方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列. 从点到面, 下面我们来看下非阻塞队列经典实现类:ConcurrentLinkedQueue (JDK1.8版) ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全的队列.当我们添加一个元素的时候,它会添加到队列的尾部,当我们

随机推荐