/**
 * 可见性问题
 * @author Snway
 *
 */
public class Visibility {
   
    private static boolean stop;
   
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(new Runnable(){
            public void run() {
                int i = 0;
                while(!stop) {
                    i++;
                }
                System.out.println("finish loop,i=" + i);
            }
        });
        t1.start();
        Thread.sleep(1000);
        stop = true;
        Thread.sleep(2000);
        System.out.println("finish main");
    }
}

在hotspot中编译后分别运行:
1、java Visibility
2、java -server Visibility

会发现第二种方式会死循环!

       可见性是关于在哪些情况下,一个线程执行的结果对另一个线程是可见的问题。在本例中的问题正是由于主线程对stop变量的写入操作结果,对t1线程是不可见的所导致的。在单线程中,如果向某个变量先写入值,然后再没有其他写入操作的情况下读取这个变量,那么总能得到相同的值。但是,当读操作与写操作在不同的线程中执行时,情况却并非如此。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。在本例中,可对声明变量stop时加上volatile修正可见性问题。

只有在下列情况时,一个线程对字段的修改才能确保对另一个线程可见:

一个写线程释放一个锁之后,另一个读线程随后获取了同一个锁。本质上,线程释放锁时会将强制刷新工作内存中的脏数据到主内存中,获取一个锁将强制线程装载(或重新装载)字段的值。锁提供对一个同步方法或块的互斥性执行,线程执行获取锁和释放锁时,所有对字段的访问的内存效果都是已定义的。

注意同步的双重含义:锁提供高级同步协议,同时在线程执行同步方法或块时,内存系统(有时通过内存屏障指令)保证值的一致性。这说明,与顺序程序设计相比较,并发程序设计与分布式程序设计更加类似。同步的第二个特性可以视为一种机制:一个线程在运行已同步方法时,它将发送和/或接收其他线程在同步方法中对变量所做的修改。从这一点来说,使用锁和发送消息仅仅是语法不同而已。

如果把一个字段声明为volatile型,线程对这个字段写入后,在执行后续的内存访问之前,线程必须刷新这个字段且让这个字段对其他线程可见(即该字段立即刷新)。每次对volatile字段的读访问,都要重新装载字段的值。

一个线程首次访问一个对象的字段,它将读到这个字段的初始值或被某个线程写入后的值。
        此外,把还未构造完成的对象的引用暴露给某个线程,这是一个错误的做法 (see ?.1.2)。在构造函数内部开始一个新线程也是危险的,特别是这个类可能被子类化时。Thread.start有如下的内存效果:调用start方法的线程释放了锁,随后开始执行的新线程获取了这个锁。如果在子类构造函数执行之前,可运行的超类调用了new Thread(this).start(),当run方法执行时,对象很可能还没有完全初始化。同样,如果你创建且开始一个新线程T,这个线程使用了在执行start之后才创建的一个对象X。你不能确信X的字段值将能对线程T可见。除非你把所有用到X的引用的方法都同步。如果可行的话,你可以在开始T线程之前创建X。

线程终止时,所有写过的变量值都要刷新到主内存中。比如,一个线程使用Thread.join来终止另一个线程,那么第一个线程肯定能看到第二个线程对变量值得修改。

注意,在同一个线程的不同方法之间传递对象的引用,永远也不会出现内存可见性问题。
内存模型确保上述操作最终会发生,一个线程对一个特定字段的特定更新,最终将会对其他线程可见,但这个“最终”可能是很长一段时间。线程之间没有同步时,很难保证对字段的值能在多线程之间保持一致(指写线程对字段的写入立即能对读线程可见)。特别是,如果字段不是volatile或没有通过同步来访问这个字段,在一个循环中等待其他线程对这个字段的写入,这种情况总是错误的(see ?.2.6)。

在缺乏同步的情况下,模型还允许不一致的可见性。比如,得到一个对象的一个字段的最新值,同时得到这个对象的其他字段的过期的值。同样,可能读到一个引用变量的最新值,但读取到这个引用变量引用的对象的字段的过期值。
        不管怎样,线程之间的可见性并不总是失效(指线程即使没有使用同步,仍然有可能读取到字段的最新值),内存模型仅仅是允许这种失效发生而已。因此,即使多个线程之间没有使用同步,也不保证一定会发生内存可见性问题(指线程读取到过期的值),java内存模型仅仅是允许内存可见性问题发生而已。在很多当前的JVM实现和java执行平台中,甚至是在那些使用多处理器的JVM和平台中,也很少出现内存可见性问题。共享同一个CPU的多个线程使用公共的缓存,缺少强大的编译器优化,以及存在强缓存一致性的硬件,这些都会使线程更新后的值能够立即在多线程之间传递。这使得测试基于内存可见性的错误是不切实际的,因为这样的错误极难发生。或者这种错误仅仅在某个你没有使用过的平台上发生,或仅在未来的某个平台上发生。这些类似的解释对于多线程之间的内存可见性问题来说非常普遍。没有同步的并发程序会出现很多问题,包括内存一致性问题。

Java内存模型——可见性的更多相关文章

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

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

  2. Java内存模型JMM与可见性

    Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...

  3. Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

  4. Java内存模型与共享变量可见性

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:本文主要参考自<深入理解Java虚拟机(第二版)>和<深入理解Java内存模型> ...

  5. 附1 Java内存模型与共享变量可见性

    注:本文主要参考自<深入理解Java虚拟机(第二版)>和<深入理解Java内存模型> 1.Java内存模型(JMM) Java内存模型的主要目标:定义在虚拟机中将变量存储到内存 ...

  6. Java内存模型以及线程安全的可见性问题

    Java内存模型 VS JVM运行时数据区 首先Java内存模型(JMM)和JVM运行时数据区并不是一个东西,许多介绍Java内存模型的文章描述的堆,方法区,Java虚拟机栈,本地方法栈,程序计数器这 ...

  7. 02 | Java内存模型:看Java如何解决可见性和有序性问题

    什么是 Java 内存模型? 导致可见性的原因是缓存,导致有序性的原因是编译优化,那解决可见性. 有序性最直接的办法就是禁用缓存和编译优化,但是这样问题虽然解决了,我们程序的性能可就堪忧了.   合理 ...

  8. Java内存模型(三)原子性、内存可见性、重排序、顺序一致性、volatile、锁、final

          一.原子性 原子性操作指相应的操作是单一不可分割的操作.例如,对int变量count执行count++d操作就不是原子性操作.因为count++实际上可以分解为3个操作:(1)读取变量co ...

  9. Java内存模型之可见性问题

    本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 前言 之前的文章中讲到,JMM是内存模型规范在Java语 ...

随机推荐

  1. unity3d切换场景时,背景音乐保持播放

    首先创建两个场景: One,Two 再创建一个空游戏对象: GameObject,并添加AudioSource组件,把要播放的音乐拖放进去 给GameObject添加脚本AlwayAudio,代码如下 ...

  2. NOIP2011 计算系数

    1计算系数 给定一个多项式 (ax + by)k ,请求出多项式展开后 x n y m 项的系数. [输入] 输入文件名为 factor.in. 共一行,包含 5 个整数,分别为 a,b,k,n,m, ...

  3. NOIP2014 无线网络发射器选址

    1.无线网络发射器选址 (wireless.cpp/c/pas) [问题描述] 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平 ...

  4. [原创]git使用入门

    创建git项目并初始化 建立一个新文件夹,然后将该文件夹定义为git项目 Lilis-MacBook-Pro:GitDir lili$ mkdir testgit Lilis-MacBook-Pro: ...

  5. Delphi给窗体镶边-为控件加边框,描边,改变边框颜色

    PS:因为我现在用的电脑是WIN7 64位系统,所以没有实现功能,不知道XP是否可行. //1.定义方法 procedure WMNCPaint(var Msg : TWMNCPaint); mess ...

  6. 射频识别技术漫谈(13)——Mifare S50与S70【worldisng笔记】

    Mifare S50和Mifare S70又常被称为Mifare Standard.Mifare Classic.MF1,是遵守ISO14443A标准的卡片中应用最为广泛.影响力最大的的一员.而Mif ...

  7. Intent简介

    1 Intent概念 1.1 Intent的作用 指明Intent所要启动的对象 提供将要启动对象组件运行需要的数据 组件类型 启动方法 Activity startActivity(Intent i ...

  8. java中几个特殊的类

    1 内部类-----是在其他类中的类 1.1 内部类的定义 将类定义为 class xxxx{... ...}置于某个类的内部即可‘ 内部类编译时生成 xxxx$xxxx的class文件 内部类不能够 ...

  9. 剑指OFFER之第一个只出现一次的字符(九度OJ1283)

    题目描述: 在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符. 输入: 输入有多组数据每一组输入一个字符串. 输出: 输出第一个只出现一次的字 ...

  10. xcode4的环境变量,Build Settings参数,workspace及联编设置

    转自:http://www.cnblogs.com/lancidie/archive/2013/04/08/3007566.html 一.xcode4中的环境变量 $(BUILT_PRODUCTS_D ...