为什么StampedLock会导致CPU100%?
StampedLock 是 Java 8 引入的一种高级的锁机制,它位于 java.util.concurrent.locks 包中。与传统的读写锁(ReentrantReadWriteLock)相比,StampedLock 提供了更灵活和更高性能的锁解决方案,尤其适用于读操作远多于写操作的场景。
1.特点展示
相比于 Java 中的其他锁,StampedLock 具有以下特点:
- 读写分离:StampedLock 支持读写分离,读锁和写锁可以同时被不同的线程持有,从而提高了并发性能。而 synchronized 和 ReentrantLock 则不支持读写分离,读操作和写操作会相互阻塞。
- 乐观读:StampedLock 支持乐观读,读操作不会阻塞写操作,只有在写操作发生时才会升级为悲观读。这种方式适用于读多写少的场景,可以提高读操作的并发性能。而 ReentrantReadWriteLock 则不支持乐观读。
- 不可重入:ReentrantLock 和 synchronized 都是可重入锁,而 StampedLock 的写锁是不可重入的。
- 性能优势:StampedLock 在多线程并发中的读多情况下有更好的性能,因为 StampedLock 获取乐观读锁时,不需要通过 CAS 操作来设置锁的状态,只是简单地通过测试状态即可。
2.基本使用
StampedLock 有三种读写方法:
- readLock:读锁,用于多线程并发读取共享资源。
- writeLock:写锁,用于独占写入共享资源。
- 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 时,需要注意以下几个问题:
- 不可重入性:StampedLock 的读锁和写锁都不支持重入,这意味着一个线程在获取了锁之后,不能再次获取同一个锁,所以在使用 StampedLock 时,一定要避免嵌套使用。
- 死锁问题:使用 StampedLock 时,必须使用与获取锁时相同的 stamp 来释放锁,否则就会导致释放锁失败,从而导致死锁问题的发生。
- 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%?的更多相关文章
- 并发场景下HashMap死循环导致CPU100%的问题
参考链接:并发场景下HashMap死循环导致CPU100%的问题
- 黄聪:Wordpress程序Mysql查询导致CPU100%,页面错误增量飙高解决方案
最近做的网站,访客一多,CPU就会飙到100%,找了网上很多解决方案,用了下面的方法进行优化: 1.mysql开启慢查询方法: 在my.ini的[mysqld]添加如下语句: log-slow-que ...
- php用户量剧增导致cpu100%解决办法
在php扩展里边开启opcache扩展,此扩展是解析php的缓存机制,每次解析都要消耗cpu,所以有大量的fpm进程去占用cpu,开启此扩展之后cpu就瞬间下来了,只解析第一次,往后的都使用缓存.很好 ...
- 利用windbg查找dictionary导致IIS占CPU100%案例分析(一)
一.背景 先说下windbg使用场景.各位coder在工作中或多或少都会遇到下面四种情况 1.本地代码好好的,放服务器上运行一段时间后,IIS服务突然占用 w3wp.exe CPU突然100% ,不得 ...
- HashMap-死锁导致cpu占用100%分析(转)
最近项目里面的一段千年代码出了问题,这个问题以前也出现过,不过不是那么明显,这次迁移机器由以前的4台机子变成2台以后问题被放大,最终不得不解决,特此分析一下. 先放出问题的代码 ? 1 2 3 4 5 ...
- 阿里云被挖矿使用,导致cpu长期处于100%,ddgs进程,xWx3T进程,关于redis密码
1.使用top命令,查看到一个叫xWx3T的进程cpu占用99.8%,由于我的阿里云是单核的,所以最高只能100%. 把它用kill命令杀死后,过一会儿又启动了,又占用100%. 使用ps -ef可以 ...
- Dictionary导致IIS CPU 100%案例分析 学会使用WinDbg工具
.NET 开发注意 线程安全性问题.弄不好可能会导致CPU满载 特别主要 Dictionary作为静态变量使用的情况. 解决方法: Dictionary 换成 ConcurrentDictiona ...
- 二:基础概述netty
如果不了解netty的,可以百度下,netty社区现在也比较活跃. 现在所谓的大数据,flume,storm等底层都是netty. netty的性能模型: io模型---->异步非阻塞io ...
- netty学习
1.不选择java原生nio的原因 (1)nio的类库和api繁杂 (2)需要具备其他的额外的技能做铺垫,例如熟悉java多线程编程. (3)可靠性能力补齐的工作量和难度都非常大,例如客户 ...
- Java IO编程全解(六)——4种I/O的对比与选型
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面讲到:Java IO编程全解(五)--AIO编程 为了防止由于对一些技术概念和术语 ...
随机推荐
- C# WPF 自定义Main方法总结
在使用自定义的Main函数启动应用时,应该需要做这几步: 1.去掉App.xaml的Application的starup属性. 2.右键App.xaml,属性 把生成操作改为Page. 3.如果有引入 ...
- wpf 斗地主 单机版 没有机器人出牌算法
斗地主的游戏流程实现了,剩余的音效和机器人的出牌算法,抓地主算法就用最简单的实现. 主要实现了各种牌组的组合,牌组的大小比较,总共有16种牌组 基础牌组 单张.炸弹.炸弹型飞机带对子.炸弹型飞机什么都 ...
- 使用 TestContainers 进行数据库集成测试
在软件开发过程中,集成测试是至关重要的一环.它确保不同组件之间的协作正常,并验证系统在整体上的功能和性能.然而,传统的集成测试往往需要依赖于外部资源,如数据库.消息队列等,这给测试环境的搭建和维护带来 ...
- C基本知识
1 C基本数据类型 C基本的数据类型说明: 2 字节序 测试代码: #include <stdio.h> typedef unsigned char *byte_pointer; void ...
- C# 机器学习
前言: 提起人工智能,机器学习.大家都是一脸懵的样子.其实呢,就是根据数据进行训练.然后可以大概的预测结果.Visual Studio2019 Preview中提供了图形界面的ML.Net,所以,只要 ...
- kettle从入门到精通 第二十二课 kettle carte web服务中文乱码
在windows 上面 carte服务的canvas画布展示的中文正常,但是在linux上面中文展示乱码,如下所示: 原因:linux 机器缺少字体所致. kettle源码中使用字体: 解决方法: 安 ...
- 爬取m3u8加密视频
import random import os import re import requests import asyncio import aiohttp import time from lxm ...
- Vue学习:2.V标签综合2
接上一篇... V标签综合使用:书架案例 功能: 实现列表的渲染和删除 思路: 使用 v-for 渲染数据列表,并在每个列表项内放置一个绑定了 del方法的"删除"按钮,点击按钮时 ...
- java.util.Date和java.sql.Date有什么区别?
java.util.Date包含日期和时间,而java.sql.Date只包含日期信息,而没有具体的时间信息.如果你想把时间信息存储在数据库 里,可以考虑使用Timestamp或者Date ...
- Thread.sleep 延时查询或延时查询前更新es缓存数据
Thread.sleep 延时查询或延时查询前更新es缓存数据 MQ消息的顺序性,或发送MQ的发送端未严格事务处理,可能存在数据未落库的情况,而导致接收端处理MQ消息的时候,查询为空. //demo1 ...