趣谈Java变量的可见性问题
了解过多线程的我们,对synchorized,sleep和valatile都比较了解,但是当这三个名词和“Java变量得可见性”的话题联系在一起不知道大家是否还可以保持大脑清晰???
最近看到一个关于Java变量可见性的问题,感觉比较新鲜就查了一些资料,下面分享给大家:
首先给大家看一段关于多线程的执行代码:
- package com.test;
- import java.util.concurrent.TimeUnit;
- public class test1 {
- private static boolean is = true;
- public static void main(String[] args) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- int i = 0;
- while(test1.is){
- i++;
- 1 //synchronized (this) { } 会强制刷新主内存的变量值到线程栈?
- 2 //System.out.println("1"); println 是synchronized 的,会强制刷新主内存的变量值到线程栈?
- 3 //sleep 会从新load主内存的值?
- // try {
- // TimeUnit.MICROSECONDS.sleep(1);
- // }catch (InterruptedException e) {
- // e.printStackTrace();
- // }
- }
- }
- }).start();
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- new Thread(new Runnable() {
- @Override
- public void run() {
- is = false; //设置is为false,使上面的线程结束while循环
- }
- }).start();
- }
- }
在运行当前代码时,我们会发现程序可以正常运行,但是如果取消注释行(1-3)中的任意一行,程序都会终止,我们不免产生疑问:为什么?synchronized会强制刷新主内存的变量值到线程栈??那么sleep是干嘛的?
下面一起来解决代码执行过程中产生的问题:
Q1:为什么注释代码后程序不会终止?
A1:因为 boolean is=true 的变量值被前面线程(简称线程A)加载到自己的工作内存,在后面的线程(简称线程B)改变 boolean is=false 之后不一定会立马写入主存(不过这道题中应该会马上写入主存,因为线程执行完 is=false之后线程就要退出了),即便立马写入了主存后线程A也不一定马上load到工作内存中,所以程序一直不会终止?这个是我们大多数人想到的,但其实JVM针对现在的硬件水平已经做了很大程度的优化,基本上很大程度的保障了工作内存和主内存的及时同步,相当于默认使用了volatile。但只是最大程度!在CPU资源一直被占用的时候,工作内存与主内存中间的同步,也就是变量的可见性就会不那么及时!后面会验证结论。
Q2:为什么取消注释中的任意一个代码块(1,2,3)程序就会终止??
A2:行号为1,2的代码有一个共同点:都涉及到了synchronized同步锁,那么是否会像小编在文章开始时产生的质疑“synchronized会强制刷新主内存的变量值到线程栈”?或者说是sleep方法会刷新主存的变量值到线程栈吗?
事实上,我们都知道synchronized只会保证在同步块中的变量的可变性,而is变量并不在该同步块中,所以显然不是这个导致的;那么我们可以尝试在“i++” 的代码后面加上下面的代码:
- for(int k=0;k<100000;k++){
- new Object();
- }
这时,我们重新运行程序,程序会立刻终止,这又是为什么?
因为我们知道当CPU在被占用的时候,数据的可见性得不到很好的保证。就像上面的例子中,没有添加代码之前,程序会一直循环做i++操作,所以CPU会被运算占用;而对于大量的new Object()操作来说,CPU已经不是主要站时间的操作,真正的耗时应该在内存的分配上(因为CPU的处理速度明显快过内存,不然也不会有CPU的寄存器了),所以CPU空闲后会遵循jvm优化基准,尽可能快的保证数据的可见性,从而从主存将is变量同步到工作内存中,最终导致程序的结束,这也就是sleep()方法虽然没有涉及到同步操作,却依然可以让程序终止的原因,因为sleep()方法会释放CPU,但并不会释放锁;
后记
➷ 知识扩展:
volatile:此关键字保证了变量在线程的可见性,所有线程访问由volatile修饰的变量,都必须从主存中读取后操作,并在工作内存修改后立即写回主存,保证了其他线程的可见性,同样效果的关键字还有final。
synchronized:所有同步操作都必须保证 1、原子性 2、可见性,所以在同步块中发生的变化会立马写回主存
sleep:此方法只会让出CPU执行时间,并不会释放锁;
➷ 寄语
技术在于不断的发现和总结,我们需要将自己输入的知识进行整理后输出,这样才能对输入的知识更好的理解;以上纯属个人理解,如有错误,请大家指正;
趣谈Java变量的可见性问题的更多相关文章
- 关于Java变量的可见性问题
转自:http://www.importnew.com/19434.html 博文前提 最近在oschina问答板块看到了一个关于java变量在工作内存和主存中的可见性问题:synchorized,s ...
- 浅谈Java变量的初始化顺序详解
规则1(无继承情况下):对于静态变量.静态初始化块.变量.初始化块.构造器,它们的初始化顺序依次是(静态变量.静态初始化块)>(变量.初始化块)>构造器证明代码: 复制代码 代码如下: p ...
- Java多线程中变量的可见性
之所以写这篇博客, 是因为在csdn上看到一个帖子问的就是这个问题. 废话不多说, 我们先看看他的代码(为了减少代码量, 我将创建线程并启动的部分修改为使用方法引用). 1 2 3 4 5 6 7 8 ...
- 浅谈Java和JavaScript中变量和数据类型的区别
对于一门编程语言的学习,如果第一步是安装环境,那么第二步一定是学习这门语言的基本规则,变量和数据类型则首当其冲 JavaScipt作为一个蹭Java热度而命名的语言,在很多方面和Java也有一定的相似 ...
- 沉淀再出发:再谈java的多线程机制
沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于ja ...
- 浅谈Java的内存模型以及交互
本文的内存模型只写虚拟机内存模型,物理机的不予描述. Java内存模型 在Java中,虚拟机将运行时区域分成6中,如下图: 程序计数器:用来记录当前线程执行到哪一步操作.在多 ...
- 浅谈Java中的equals和==(转)
浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...
- 浅谈Java中的对象和引用
浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...
- 浅谈Java中的equals和==
浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...
随机推荐
- ****CI和UEditor集成
百度UEditor是一款比较常用编辑器 下载地址: http://ueditor.baidu.com/website/download.html 1.在assets目录下建立ueditor文件夹,把下 ...
- 利用word的VBA,为代码统一表格宽度,底色及行号
如果文档中的代码表格时,感觉还是很快速有用的. Sub HangHao() ' ' hanghao 宏 ' ' Dim parag As Paragraph Dim nLineNum: nLineNu ...
- 028.Zabbix常见故障
一 中文乱码处理 1.1 现象 1.2 解决方法 将任意一中文字体上传至/usr/share/zabbix/fonts,如微软雅黑. vi /usr/share/zabbix/include/defi ...
- 自己的reset.css
复制.粘贴 /* http://www.cnblogs.com/ele-cat Reset Stylesheet v1.0.1 2018-05-08 Author: Ele-cat - http:// ...
- 使用Metasploit工作区
使用Metasploit工作区 Metasploit将所有数据都存储在PostgeSQL服务器中的msf数据库.渗透测试人员经常要使用Metasploit同时执行多个任务.为了避免数据混杂,Met ...
- iOS 11开发教程(十七)iOS11应用视图之使用按钮接收用户输入
iOS 11开发教程(十七)iOS11应用视图之使用按钮接收用户输入 在iOS中提供了很多的控件以及视图来丰富用户界面,对于这些视图以及控件我们在上一章中做了简单的介绍.本章我们将详细讲解这些视图. ...
- SQL2008配置管理工具服务显示远程过程调用失败
问题: 打开SQL2008配置管理工具,发现SQL服务名称里什么也没有,只有一个提示: 解决办法: 这是由于电脑中安装有Visual Stuido, 它内含一个本地SQL数据库服务:Microso ...
- leetcode 两数之和 python
两数之和 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 1 ...
- android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
- BZOJ.1028.[JSOI2007]麻将(贪心)
题目链接 枚举对子,枚举每张牌,先出完它的刻子,剩下的出顺子.\(O(n^3)\). 不是这样 -> 出完所有刻子,最后出顺子.(日常zz) 优先仨相同的,然后顺子,有一次且一定要用一次机会补顺 ...