导致并发程序出现问题的根本原因 左手的ㄟ右手 2024-04-24 23:02 32阅读 0赞 并发程序出现问题的根本原因通常归结为两个核心问题:竞争条件(Race Condition)和内存可见性(Memory Visibility)。这些问题是由于多线程同时访问和修改共享资源而没有进行适当同步所导致的。现在让我们更详细地探讨这些问题。 #### 竞争条件(Race Condition) #### 竞争条件发生在多个线程或进程在没有适当同步机制的情况下同时访问和修改共享数据。当多个线程访问和修改数据,最终结果依赖于线程执行的精确时序,这可能导致不一致和预期之外的结果。 ##### 示例代码 ##### public class RaceConditionExample { private static int sharedVar = 0; public static void main(String[] args) throws InterruptedException { Thread incrementer = new Thread(() -> { for (int i = 0; i < 1_000; i++) { sharedVar++; } }); Thread decrementer = new Thread(() -> { for (int i = 0; i < 1_000; i++) { sharedVar--; } }); incrementer.start(); decrementer.start(); incrementer.join(); decrementer.join(); System.out.println("Final value: " + sharedVar); } } 在没有适当同步的情况下运行这段代码可能会得到不同的输出结果,因为 `sharedVar++` 和 `sharedVar--` 都不是原子操作。实际上,每个操作可以分解为三个步骤:读取值、修改值、写回新值,线程在这些步骤之间的交替执行导致了不一致的结果。 #### 内存可见性(Memory Visibility) #### 内存可见性问题发生在一个线程修改的共享变量的新值对于其他线程不可见。这是由于线程缓存导致的,线程可以在自己的本地内存(如 CPU 缓存)中保留共享变量的副本,而不是直接在主存中进行读写。 ##### 示例代码 ##### public class VisibilityExample { private static boolean ready = false; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } } 在这个例子中,没有适当的同步机制,没有办法保证主线程写入 `ready` 和 `number` 变量的值对启动的 `ReaderThread` 线程立即可见。因此,即使 `ready` 被设置为 `true`,`ReaderThread` 线程也可能看不到这个改变,导致它无限循环。 #### 解决方案 #### 为了解决这些问题,我们需要使用同步机制,如 `synchronized` 关键字、`ReentrantLock`、`Atomic` 变量类、`volatile` 关键字等,以确保操作的原子性和内存的可见性。 ##### 改进竞争条件示例 ##### public class SynchronizedRaceConditionExample { private static int sharedVar = 0; private static synchronized void increment() { sharedVar++; } private static synchronized void decrement() { sharedVar--; } public static void main(String[] args) throws InterruptedException { Thread incrementer = new Thread(() -> { for (int i = 0; i < 1_000; i++) { increment(); } }); Thread decrementer = new Thread(() -> { for (int i = 0; i < 1_000; i++) { decrement(); } }); incrementer.start(); decrementer.start(); incrementer.join(); decrementer.join(); System.out.println("Final value: " + sharedVar); } } ##### 改进内存可见性示例 ##### public class VolatileVisibilityExample { private static volatile boolean ready = false; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } } 在这些改进的例子中,我们用 `synchronized` 关键字和 `volatile` 关键字提供了必要的同步机制。`synchronized` 保证了只有一个线程可以执行 `increment` 或 `decrement` 方法,实现了原子性;而 `volatile` 保证了 `ready` 变量的改变对所有线程立即可见,解决了内存可见性问题。 这些同步技术可以有效地帮助避免并发程序中的问题,但使用它们也需要谨慎,因为不当的使用可能会导致死锁等其他并发问题,或者因过度同步导致性能问题。
相关 频繁出现的Java并发问题实例 在Java并发编程中,常见的问题有线程阻塞、死锁、资源泄露、无序执行等。 1. 线程阻塞: ```java // 错误例子:线程A无限等待线程B完成任务 T 电玩女神/ 2024年09月11日 15:36/ 0 赞/ 19 阅读
相关 导致并发程序出现问题的根本原因 并发程序出现问题的根本原因通常归结为两个核心问题:竞争条件(Race Condition)和内存可见性(Memory Visibility)。这些问题是由于多线程同时访问... 左手的ㄟ右手/ 2024年04月24日 23:02/ 0 赞/ 33 阅读
相关 导致容器oom的原因 容器(container)内存溢出(out of memory,OOM)的原因通常有以下几个: 1. 容器内存限制不足:容器在创建时可以指定内存限制,如果容器分配的内存超过 秒速五厘米/ 2024年03月25日 12:19/ 0 赞/ 15 阅读
相关 JX8NET 小游戏网教你查找问题根本原因 来源:[JX8NET 小游戏网教你查找问题根本原因][JX8NET] JX8NET 小游戏网在开发程序时,会遇到各种各样的问题,如何解决这些问题呢?这里跟大家分享一个方法: 逃离我推掉我的手/ 2022年08月23日 13:52/ 0 赞/ 115 阅读
相关 springmvc的出现原因 1.springmvc是方法级别的拦截,一个方法对应一个request, 而且每个方法之间没有资源共享相互独立(各自通过自己的modelMap返回给框架) 2. struts 系统管理员/ 2022年06月10日 06:57/ 0 赞/ 128 阅读
相关 现代 js 框架存在的根本原因 ![t01c3f0ef32ffdd822c.jpg][] 我曾见过很多很多人盲目地使用(前端)框架,如 React,Angular 或 Vue等等。这些框架提供了许多有 男娘i/ 2022年05月19日 10:23/ 0 赞/ 146 阅读
相关 程序出现bug是必然出现的情况还是程序猿水平有限导致的? > > 原创文章,欢迎转载。转载请注明:转载自[IT人故事会][IT],谢谢! > > 原文链接地址:[程序出现bug是必然出现的情况还是程序猿水平有限导致的?][bug] ╰半橙微兮°/ 2022年05月08日 03:18/ 0 赞/ 80 阅读
相关 并发程序的宏观性问题 主要有三个问题: 安全性问题,活跃性问题,性能问题。 首先安全性问题,表现是怎么样的呢?我们写并发程序时,希望程序按照我们期望的执行顺序来执行,但是他们都没按照我们期望的 素颜马尾好姑娘i/ 2022年01月22日 13:05/ 0 赞/ 116 阅读
相关 有可能导致HttpQueryInfo 执行时出现12150 错误的一个原因 WInInet 是微软对FTP HTTP 协议做封装库。通过WInInet 系列函数可以方便的实现基于HTTP FTP协议的文件上传下载。最近做了一个项目也是用到了WInIne 待我称王封你为后i/ 2021年12月01日 23:24/ 0 赞/ 328 阅读
还没有评论,来说两句吧...