yyds ,Java中如何保证线程安全性 ,@_@或许是你想知道的 下
四、可见性---volatile
对于可见性,JVM提供了synchronized和volatile。这里我们看volatile。
(1)volatile的可见性是通过内存屏障和禁止重排序实现的
volatile会在写操作时,会在写操作后加一条store屏障指令,将本地内存中的共享变量值刷新到主内存:
volatile在进行读操作时,会在读操作前加一条load指令,从内存中读取共享变量:
(2)但是volatile不是原子性的,进行++操作不是安全的
@Slf4j public class VolatileExample { // 请求总数 public static int clientTotal = 5000; // 同时并发执行的线程数 public static int threadTotal = 200; public static volatile int count = 0; public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}", count); } private static void add() { count++; } }
执行后发现线程不安全,原因是 执行conut++ 时分成了三步,第一步是取出当前内存 count 值,这时 count 值时最新的,接下来执行了两步操作,分别是 +1 和重新写回主存。假设有两个线程同时在执行 count++ ,两个内存都执行了第一步,比如当前 count 值为 5 ,它们都读到了,然后两个线程分别执行了 +1 ,并写回主存,这样就丢掉了一次加一的操作。
(3)volatile适用的场景
既然volatile不适用于计数,那么volatile适用于哪些场景呢:
1.对变量的写操作不依赖于当前值
2.该变量没有包含在具有其他变量不变的式子中
因此,volatile适用于状态标记量:
线程1负责初始化,线程2不断查询inited值,当线程1初始化完成后,线程2就可以检测到inited为true了。另外,关注Java知音公众号,回复“后端面试”,送你一份面试题宝典!
五、有序性
有序性是指,在JMM中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
可以通过volatile、synchronized、lock保证有序性。
另外,JMM具有先天的有序性,即不需要通过任何手段就可以得到保证的有序性。这称为happens-before原则。
如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性。虚拟机可以随意地对它们进行重排序。
happens-before原则:
- 程序次序规则:在一个单独的线程中,按照程序代码书写的顺序执行。
- 锁定规则:一个unlock操作happen—before后面对同一个锁的lock操作。
- volatile变量规则:对一个volatile变量的写操作happen—before后面对该变量的读操作。
- 线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。
- 线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
- 线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。
- 对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。
- 传递性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。
总结:
如上,技术在于交流流通,/握手
原文作者:高一级花阿梨喜欢吃榴莲
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的