一、读写锁

在我的《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;
}

欢迎关注我的博客,更多精品知识合集

本文转载注明出处(必须带连接,不能只转文字):字母哥博客 - zimug.com

觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力!。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。

最新文章

  1. LeetCode 笔记26 Single Number II
  2. Bugfree实用心得_转
  3. The connection to adb is down, and a severe error has occured.
  4. Linux From Scratch [2]
  5. C# http下载(支持断点续传)
  6. jquery之前后台交互
  7. Apple Swift 中文教程 高速參考 基本的语法
  8. CentOS7 配置花生壳开机启动
  9. DG环境的日常巡检
  10. 安装mongodb服务
  11. 2018.5.15Html标签初学
  12. python-作用域解析
  13. 【目录】LeetCode Java实现
  14. datetime学习
  15. ML(5)——神经网络1(神经元模型与激活函数)
  16. sass和scss相关知识
  17. STM32内部flash存储小数——别样的C语言技巧
  18. Spacy 使用
  19. .NET在IE10下的回传BUG修复
  20. JavaScript里的创建对象(一)

热门文章

  1. centos 安装solr6
  2. idea中web项目的创建
  3. 攻防世界杂项MISCall
  4. 无人驾驶—高精地图和V2X
  5. IdentityServer4系列 | 支持数据持久化
  6. ionic3 教程(五)基本的网络请求
  7. 重磅:前端 MVVM 与 FRP 的升阶实践 —— ReRest 可视化编程
  8. Unity用Input.touches实现手机端多点触控
  9. 彻底弄懂小程序e.target与e.currentTarget
  10. Windows中Nginx配置nginx.conf不生效解决方法(路径映射)