StampedLock 是 Java 8 引入的一种高级的锁机制,它位于 java.util.concurrent.locks 包中。与传统的读写锁(ReentrantReadWriteLock)相比,StampedLock 提供了更灵活和更高性能的锁解决方案,尤其适用于读操作远多于写操作的场景。

1.特点展示

相比于 Java 中的其他锁,StampedLock 具有以下特点:

  1. 读写分离:StampedLock 支持读写分离,读锁和写锁可以同时被不同的线程持有,从而提高了并发性能。而 synchronized 和 ReentrantLock 则不支持读写分离,读操作和写操作会相互阻塞。
  2. 乐观读:StampedLock 支持乐观读,读操作不会阻塞写操作,只有在写操作发生时才会升级为悲观读。这种方式适用于读多写少的场景,可以提高读操作的并发性能。而 ReentrantReadWriteLock 则不支持乐观读。
  3. 不可重入:ReentrantLock 和 synchronized 都是可重入锁,而 StampedLock 的写锁是不可重入的。
  4. 性能优势:StampedLock 在多线程并发中的读多情况下有更好的性能,因为 StampedLock 获取乐观读锁时,不需要通过 CAS 操作来设置锁的状态,只是简单地通过测试状态即可。

2.基本使用

StampedLock 有三种读写方法:

  1. readLock:读锁,用于多线程并发读取共享资源。
  2. writeLock:写锁,用于独占写入共享资源。
  3. tryOptimisticRead:读乐观锁,用于在不阻塞其他线程的情况下尝试读取共享资源。

其中 readLock() 和 writeLock() 方法与 ReentrantReadWriteLock 的用法类似,而 tryOptimisticRead() 方法则是 StampedLock 引入的新方法,它用于非常短的读操作。

因此,我们在加锁时,可以使用性能更高的读乐观锁来替代传统的读锁,如果能加锁成功,则它可以和其他线程(即使是写操作)一起执行,也无需排队运行(传统读锁遇到写锁时需要排队执行),这样的话 StampedLock 的执行效率就会更高,它是使用如下:

// 创建 StampedLock 实例
StampedLock lock = new StampedLock();
// 获取乐观读锁
long stamp = lock.tryOptimisticRead();
// 读取共享变量
if (!lock.validate(stamp)) { // 检查乐观读锁是否有效
stamp = lock.readLock(); // 如果乐观读锁无效,则获取悲观读锁
try {
// 重新读取共享变量
} finally {
lock.unlockRead(stamp); // 释放悲观读锁
}
} // 获取悲观读锁
long stamp = lock.readLock();
try {
// 读取共享变量
} finally {
lock.unlockRead(stamp); // 释放悲观读锁
} // 获取写锁
long stamp = lock.writeLock();
try {
// 写入共享变量
} finally {
lock.unlockWrite(stamp); // 释放写锁
}

使用乐观读锁的特性可以提高读操作的并发性能,适用于读多写少的场景。如果乐观读锁获取后,在读取共享变量前发生了写入操作,则 validate 方法会返回 false,此时需要转换为悲观读锁或写锁重新访问共享变量。

3.注意事项

在使用 StampedLock 时,需要注意以下几个问题:

  1. 不可重入性:StampedLock 的读锁和写锁都不支持重入,这意味着一个线程在获取了锁之后,不能再次获取同一个锁,所以在使用 StampedLock 时,一定要避免嵌套使用。
  2. 死锁问题:使用 StampedLock 时,必须使用与获取锁时相同的 stamp 来释放锁,否则就会导致释放锁失败,从而导致死锁问题的发生。
  3. CPU 使用率飙升问题:如果 StampedLock 使用不当,具体来说,在 StampedLock 执行 writeLock 或 readLock 阻塞时,如果调用了中断操作,如 interrupt() 可能会导致 CPU 使用率飙升。这是因为线程接收到了中断请求,但 StampedLock 并没有正确处理中断信号,那么线程可能会陷入无限循环中,试图从中断状态中恢复,这可能会导致 CPU 使用率飙升。

4.CPU 100%问题演示

以下代码中线程 2 会导致 CPU 100% 的问题,如下代码所示:

public void runningTask() throws Exception{
final StampedLock lock = new StampedLock();
Thread thread = new Thread(()->{
// 获取写锁
lock.writeLock();
// 永远阻塞在此处,不释放写锁
LockSupport.park();
});
thread.start(); // 保证 thread 获取写锁
Thread.sleep(100);
Thread thread2 = new Thread(()->
// 阻塞在悲观读锁
lock.readLock()
);
thread2.start();
// 保证 thread2 阻塞在读锁
Thread.sleep(100);
// 中断线程 thread2,导致 thread2 CPU 飙升
thread2.interrupt();
thread2.join();
}

以上代码中,线程一先获取到锁,之后阻塞,并未释放锁,而线程二阻塞在 readLock() 读锁时,收到了中断请求 interrupt(),但并未正确处理中断异常,因此线程会陷入无限循环中,试图从中断状态中恢复,这就会导致 CPU 使用率一直飙升。

课后思考

如何避免 StampedLock CPU 100% 的问题?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

为什么StampedLock会导致CPU100%?的更多相关文章

  1. 并发场景下HashMap死循环导致CPU100%的问题

    参考链接:并发场景下HashMap死循环导致CPU100%的问题

  2. 黄聪:Wordpress程序Mysql查询导致CPU100%,页面错误增量飙高解决方案

    最近做的网站,访客一多,CPU就会飙到100%,找了网上很多解决方案,用了下面的方法进行优化: 1.mysql开启慢查询方法: 在my.ini的[mysqld]添加如下语句: log-slow-que ...

  3. php用户量剧增导致cpu100%解决办法

    在php扩展里边开启opcache扩展,此扩展是解析php的缓存机制,每次解析都要消耗cpu,所以有大量的fpm进程去占用cpu,开启此扩展之后cpu就瞬间下来了,只解析第一次,往后的都使用缓存.很好 ...

  4. 利用windbg查找dictionary导致IIS占CPU100%案例分析(一)

    一.背景 先说下windbg使用场景.各位coder在工作中或多或少都会遇到下面四种情况 1.本地代码好好的,放服务器上运行一段时间后,IIS服务突然占用 w3wp.exe CPU突然100% ,不得 ...

  5. HashMap-死锁导致cpu占用100%分析(转)

    最近项目里面的一段千年代码出了问题,这个问题以前也出现过,不过不是那么明显,这次迁移机器由以前的4台机子变成2台以后问题被放大,最终不得不解决,特此分析一下. 先放出问题的代码 ? 1 2 3 4 5 ...

  6. 阿里云被挖矿使用,导致cpu长期处于100%,ddgs进程,xWx3T进程,关于redis密码

    1.使用top命令,查看到一个叫xWx3T的进程cpu占用99.8%,由于我的阿里云是单核的,所以最高只能100%. 把它用kill命令杀死后,过一会儿又启动了,又占用100%. 使用ps -ef可以 ...

  7. Dictionary导致IIS CPU 100%案例分析 学会使用WinDbg工具

    .NET  开发注意 线程安全性问题.弄不好可能会导致CPU满载 特别主要 Dictionary作为静态变量使用的情况. 解决方法: Dictionary 换成  ConcurrentDictiona ...

  8. 二:基础概述netty

    如果不了解netty的,可以百度下,netty社区现在也比较活跃. 现在所谓的大数据,flume,storm等底层都是netty.   netty的性能模型: io模型---->异步非阻塞io ...

  9. netty学习

    1.不选择java原生nio的原因   (1)nio的类库和api繁杂   (2)需要具备其他的额外的技能做铺垫,例如熟悉java多线程编程.   (3)可靠性能力补齐的工作量和难度都非常大,例如客户 ...

  10. Java IO编程全解(六)——4种I/O的对比与选型

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面讲到:Java IO编程全解(五)--AIO编程 为了防止由于对一些技术概念和术语 ...

随机推荐

  1. Java——成语接龙——递归算法——SpringJDBC+c3p0

      这个例子,是我练习SpringJdbc+c3p0时所写.实现了输入一个成语,得到一颗不完整的接龙树.因为我数据库里有3W多个成语,在排除了接龙树成语重复的情况下,依然不能得到全部的结果,我感觉应该 ...

  2. jQuery——动画加载页面

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script&g ...

  3. Android 13 - Media框架(10)- NuPlayer::Renderer

    关注公众号免费阅读全文,进入音视频开发技术分享群! 这一节我们来了解 NuPlayer Renderer 是如何工作,avsync 机制是如何运行的. 1.创建 Renderer void NuPla ...

  4. kubernetes自动扩缩容[HPA控制器 horizontal pod autoscaler]

    kubernetes自动扩缩容[HPA控制器 horizontal pod autoscaler] #查看当前hpa控制器版本: [root@k8s-master01 ~]# kubectl get ...

  5. ansible搭建

    ansible配置步骤 1.创建用户 2.用户提权 3.用户免密 4.cp ansible配置文件 5.配置主机清单 6.修改ansible 用户路径下的配置文件 1.创建用户(都要做) [root@ ...

  6. python实现取得成员所在的多个位置

    注:本代码主要是为了实现多个集合之间求并集时的辅助代码,简单的举个例子来说明代码的功能. 约定:例如{11: [2, 3]}表示数据11在集合2和集合3中都存在. 现有以下数据: d0 = {38:  ...

  7. Java中的变量分类(按照位置分类)

    变量按位置分 通过上面类的建立我们又得到了新的概念:成员变量和局部变量 成员变量: 可以使用基本数据类型,也可以使用引用数据类型. java中的变量在使用时必须初始化,成员变量可以不对其初始化,系统会 ...

  8. B站login-极验逆向

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 https:// ...

  9. react的反向代理

    在配置在src文件夹中setupProxy.js文件,并通过npm安装http-proxy-middleware,代理中间件模块 npm i -S http-proxy-middleware 配置反向 ...

  10. jquery checkbox的全选和反选

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...