转自:http://www.importnew.com/19434.html

博文前提

最近在oschina问答板块看到了一个关于java变量在工作内存和主存中的可见性问题:synchorized,sleep 也能达到volatile 线程可见性的目的?,大致的问题描述如下:

package com.test;

import java.util.concurrent.TimeUnit;

public class Test01 {

	private static boolean is = true;

	public static void main(String[] args) {

		new Thread(new Runnable() {

			@Override
public void run() {
int i = 0;
while (Test01.is) { i++; // byte[] bts = new byte[1024*1024*2];
// byte[] bts1 = new byte[1024*1024*1];
// byte[] bts2 = new byte[1024*1024*3]; // 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,使上面的线程结束while循环
Test01.is = false;
} }).start();
} }

问:

为什么整个程序不会终止?

为什么取消注释中的任何一个代码块(1,2,3),程序才会终止?

synchronized 会强制刷新住内存的变量值到线程栈?

sleep 会干什么呢?

涉及知识解释

  • volatile:此关键字保证了变量在线程的可见性,所有线程访问由volatile修饰的变量,都必须从主存中读取后操作,并在工作内存修改后立即写回主存,保证了其他线程的可见性,同样效果的关键字还有final。
  • synchronized:所有同步操作都必须保证 1、原子性 2、可见性,所以在同步块中发生的变化会立马写回主存
  • sleep:此方法只会让出CPU执行时间,并不会释放锁。

问题分析

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++;后面加上以下代码:

byte[] bts = new byte[1024*1024*2];
byte[] bts1 = new byte[1024*1024*1];
byte[] bts2 = new byte[1024*1024*3];

再Run,程序立刻终止!为什么?

在上面的 A1 中我们已经说了即便有JVM的优化,但当CPU一直被占用的时候,数据的可见性得不到很好的保证,就像上面的程序一直循环做i++;运算占用CPU,而为什么加上上面的代码后程序就会停止呢?因为对于几个new byte[1024*1024*1]操作来说,CPU已经不是主要占时间的操作,真正的耗时应该在内存的分配上(因为CPU的处理速度明显快过内存,不然也不会有CPU的寄存器了),所以CPU空闲后会遵循JVM优化基准,尽可能快的保证数据的可见性,从而从主存同步is变量到工作内存,最终导致程序结束,这也是为什么sleep()方法虽然没有涉及同步操作,但是依然可以使程序终止,因为sleep()方法会释放CPU,但不释放锁!

关于Java变量的可见性问题的更多相关文章

  1. 趣谈Java变量的可见性问题

    了解过多线程的我们,对synchorized,sleep和valatile都比较了解,但是当这三个名词和“Java变量得可见性”的话题联系在一起不知道大家是否还可以保持大脑清晰??? 最近看到一个关于 ...

  2. Java多线程中变量的可见性

    之所以写这篇博客, 是因为在csdn上看到一个帖子问的就是这个问题. 废话不多说, 我们先看看他的代码(为了减少代码量, 我将创建线程并启动的部分修改为使用方法引用). 1 2 3 4 5 6 7 8 ...

  3. java并发之可见性与原子性:Syncronized和volatile

    转载:http://blog.csdn.net/guyuealian/article/details/52525724 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型.     ...

  4. java并发编程可见性与线程封闭

    可见性 所谓可见性,指的是当一个线程修改了对象的状态后,其他线程能够看到该对象发生的变化.在单线程环境下,向某个变量写入值,然后在后面的操作再读取,在这个过程中该变量的值对该线程来说总是可见.但是,在 ...

  5. Java-JUC(二):Java内存模型可见性、原子性、有序性及volatile具有特性

    1.Java HotSpot JVM运行时数据区 Java内存模型即Java Memory Model,简称JMM.JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式.JVM是整 ...

  6. Java-Runoob:Java 变量类型

    ylbtech-Java-Runoob:Java 变量类型 1.返回顶部 1. Java 变量类型 在Java语言中,所有的变量在使用前必须声明.声明变量的基本格式如下: type identifie ...

  7. (七)Java 变量类型

    Java 变量类型 在Java语言中,所有的变量在使用前必须声明.声明变量的基本格式如下: type identifier [ = value][, identifier [= value] ...] ...

  8. Java——变量类型

    Java变量类型: 在Java中,所有的变量在使用前必须声明.格式: type identifier [ = value ][, identifier [ =value]-.]; type为Java数 ...

  9. 菜鸟笔记:java变量命名及峰驼式命名法

    如同酒店会给每个房间起个性化的名字一样,程序中的变量也需要用合理的名字进行管理---变量名! 需要注意,给酒店房间起名字时可以是数字,如"802",也可以是有趣的名字,如" ...

随机推荐

  1. python 全栈开发,Day119(Flask初识,Render Redirect HttpResponse,request,模板语言 Jinja2,用户登录例子,内置Session)

    一.Flask初识 首先,要看你学没学过Django 如果学过Django 的同学,请从头看到尾,如果没有学过Django的同学,并且不想学习Django的同学,轻饶过第一部分 三大主流Web框架对比 ...

  2. Linux学习笔记:ftp中binary二进制与ascii传输模式的区别

    在使用ftp传输文件时,常添加上一句: binary  -- 使用二进制模式传输文件 遂查资料,如下所获. FTP可用多种格式传输文件,通常由系统决定,大多数Linux/UNIX系统只有两种模式:文本 ...

  3. Web开发.net framework 类库中必须掌握的命名空间(或者类)【转】

    Web开发常用命名空间和类. System.Collections //命名空间包含接口和类,这些接口和类定义各种对象(如列表.队列.位数组.哈希表和字典)的集合.System.Collections ...

  4. 线程使用中常见的错误-“System.InvalidOperationException”线程间操作无效: 从不是创建控件“ ”的线程访问它。

    “System.InvalidOperationException”类型的未经处理的异常在 System.Windows.Forms.dll 中发生 其他信息: 线程间操作无效: 从不是创建控件“la ...

  5. 事件方法on()

    on()方法用来处理事件.jQuery会处理所有浏览器的兼容性问题. on()方法可以指定影响哪个事件,相当于JavaScript中的addEventListener()事件监听. on()方法有两个 ...

  6. DOM树示意图

  7. 【C++ Primer 第13章】3. 交换操作

    交换操作 class HasPtr { friend void swap(HasPtr &rhs, HasPtr &yhs); //其他成员定义 }; void swap(HasPtr ...

  8. .NET正则平衡组

    1        概述 平衡组是微软在.NET中提出的一个概念,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配..NET是目前对正则支持最完备.功能最强大的语言平台之一,而平衡组正是其强大 ...

  9. BZOJ1150 [CTSC2007]数据备份Backup 贪心 堆

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1150 题意概括 数轴上面有一堆数字. 取出两个数字的代价是他们的距离. 现在要取出k对数,(一个数 ...

  10. split应用

    /* input:"5 35 53 2 3" output:"2 3 5 35 53" */ public class RegexDemo4 { public ...