对共享可变数据不加同步,虚拟机可能会搞死你

简介: 大家都知道,在Java中,除非变量的类型是 long 或 double,否则对于变量的读取和写入操作都是原子性的。也就是说,即使多个线程同时访问并修改变量,也不会有线程安全的问题。

大家都知道,在Java中,除非变量的类型是 long 或 double,否则对于变量的读取和写入操作都是原子性的。也就是说,即使多个线程同时访问并修改变量,也不会有线程安全的问题。


于是,有人可能就会觉得,既然对变量(除long或double)的读取和写入操作是原子性的,那我们是不是在多线程情况下,也可以不考虑使用同步,直接对这些变量就行读写操作呢,因为如果要对这些简单的操作加同步,反而会影响代码性能。


这种想法是不是对的呢?我们来看看下面的代码:


public class StopThread {    private static boolean stopRequested;
    public static void main(String[] args) throws InterruptedException {        Thread backgroundThread = new Thread(() -> {          int i = 0;          while (!stopRequested)              i++;        });
        backgroundThread.start();        TimeUnit.SECONDS.sleep(1);        stopRequested = true;    }}


上面的代码实现的功能是:主线程启动backgroundThread 线程,然后自己停止一秒钟,之后主线程将 stopRequested 设置为 true,从而导致后台线程的循环终止,程序结束。


代码看上去没有上面问题,然而,在我的机器上,程序永远不会终止:后台线程永远循环!


那么问题出在哪里呢?我们在主线程中做了stopRequested = true;,变成true之后,backgroundThread 线程在while循环中判断到这一变化不是应该停止循环结束程序吗?怎么就能死循环了呢?


其实问题在于在缺乏同步的情况下,无法保证后台线程何时(如果有的话)看到主线程所做的 stopRequested 值的更改。在缺乏同步的情况下,虚拟机会优化backgroundThread 中的代码,将:


while (!stopRequested)    i++;


优化成:


if (!stopRequested)    while (true)        i++;


这种操作可以说简直是丧心病狂……,我的天,由于stopRequested的判断被提到了while循环之外,导致了我们的程序进入死循环无法停止。罪魁祸首居然是因为虚拟机的优化导致的问题。而这点确实很不容易发现。


那么怎么觉得这个问题呢?既然是因为缺乏同步导致的主线程stopRequested修改无法被backgroundThread 线程发现,那么我们就给他加上同步,代码如下:


public class StopThread {    private static boolean stopRequested;
    private static synchronized void requestStop() {        stopRequested = true;    }
    private static synchronized boolean stopRequested() {        return stopRequested;    }
    public static void main(String[] args) throws InterruptedException {        Thread backgroundThread = new Thread(() -> {          int i = 0;          while (!stopRequested())            i++;        });
        backgroundThread.start();        TimeUnit.SECONDS.sleep(1);        requestStop();    }}


我们通过对stopRequested的读写添加了同步,这样,就可以让主线程对stopRequested的修改被backgroundThread 线程看到,同时虚拟机也不再会出现上面的优化代码,而是按照正常的逻辑走,每次访问stopRequested或修改都需要获取到锁才能操作。


通过上面的修改后,强哥的机器上代码终于能够正常的终止了。可是要去修改一个变量就要去手动编写对这个变量读写的加锁代码也好麻烦,有没有更好的方法呢?


当然有了,如果 stopRequested声明为 volatile,则可以省略 stopRequested的第二个版本中的锁。它不那么冗长,而且性能可能更好。虽然 volatile 修饰符不执行互斥,但它保证任何读取字段的线程都会看到最近写入的值


public class StopThread {    private static volatile boolean stopRequested;
    public static void main(String[] args) throws InterruptedException {        Thread backgroundThread = new Thread(() -> {            int i = 0;            while (!stopRequested)                i++;        });            backgroundThread.start();        TimeUnit.SECONDS.sleep(1);        stopRequested = true;    }}


所以,对于共享可变数据,虽然对它的读写操作是原子性的,但是却不能保证由一个线程编写的值对另一个线程可见。因此,如果出现线程之间能可靠通信以及实施互斥,同步是所必需的。

相关文章
|
1月前
|
存储 安全 测试技术
网络奇谭:虚拟机中的共享、桥接与Host-Only模式解析
网络奇谭:虚拟机中的共享、桥接与Host-Only模式解析
24 0
|
Linux 测试技术 开发工具
VMware Linux虚拟机与WIN7操作系统共享无线网络上网配置
VMware Linux虚拟机与WIN7操作系统共享无线网络上网配置
104 0
|
人工智能 Ubuntu Linux
RK3568开发笔记(三):RK3568虚拟机基础环境搭建之更新源、安装网络工具、串口调试、网络连接、文件传输、安装vscode和samba共享服务
RK3568开发笔记(三):RK3568虚拟机基础环境搭建之更新源、安装网络工具、串口调试、网络连接、文件传输、安装vscode和samba共享服务
RK3568开发笔记(三):RK3568虚拟机基础环境搭建之更新源、安装网络工具、串口调试、网络连接、文件传输、安装vscode和samba共享服务
|
虚拟化
Vmware虚拟机RAC集群绑定共享磁盘方法
Vmware虚拟机RAC集群绑定共享磁盘方法
535 0
Vmware虚拟机RAC集群绑定共享磁盘方法
|
存储 算法 Java
【Android 内存优化】Java 内存模型 ( Java 虚拟机内存模型 | 线程私有区 | 共享数据区 | 内存回收算法 | 引用计数 | 可达性分析 )
【Android 内存优化】Java 内存模型 ( Java 虚拟机内存模型 | 线程私有区 | 共享数据区 | 内存回收算法 | 引用计数 | 可达性分析 )
213 0
VirtualBox虚拟机共享剪贴板无效之新解决思路
VirtualBox虚拟机共享剪贴板无效之新解决思路
644 0
|
存储 Ubuntu Linux
使用VM Tools让VMware虚拟机里的ubuntu能够共享Windows系统的文件夹
使用VM Tools让VMware虚拟机里的ubuntu能够共享Windows系统的文件夹
326 0
使用VM Tools让VMware虚拟机里的ubuntu能够共享Windows系统的文件夹
|
弹性计算 运维 数据可视化
轻量应用服务器,独享云虚拟主机、共享云虚拟主机、云服务器 ECS 的区别在哪?如何辨别?
阿里云上产品线非常多,很多用户搞不清云服务器ECS、轻量应用服务器、云虚拟主机之间的区别;特别是第一次建站的用户,往往不知道该选择哪个产品来建站。下面为您简单介绍下三者之间的区别。
1226 0
轻量应用服务器,独享云虚拟主机、共享云虚拟主机、云服务器 ECS 的区别在哪?如何辨别?
|
弹性计算 应用服务中间件 数据库
轻量应用服务器,独享云虚拟主机、共享云虚拟主机、云服务器 ECS 的区别在哪?如何辨别?
阿里云上产品线非常多,很多用户搞不清云服务器ECS、轻量应用服务器、云虚拟主机之间的区别;特别是第一次建站的用户,往往不知道该选择哪个产品来建站。下面为您简单介绍下三者之间的区别。 产品特点 独享虚拟主机, 轻量应用服务器, 云服务器 三者的区别和使用和场景https://promotion.
4193 0
|
弹性计算 应用服务中间件 数据库
轻量应用服务器,独享云虚拟主机、共享云虚拟主机、云服务器 ECS 的区别?
国内的云服务器上知名的就那么几家,腾讯云是排名前列的云服务器提供商。而且腾讯云是国内互联网龙头企业,信得过,它们的产品是值得信任的。好了,废话不多说,我现在教下新手怎样选择和购买腾讯云服务器。这篇教程我写的很详细,因为有些个人建议和忠告,帮助你不要选错。
3413 0