0 0

Java多线程变量可见性问题5

网上搜了许久,所有关于可见性问题都是用的原生类型举例。
我的疑问是,如果变量是Java.Collection,一个线程调用了add方法。不加同步机制,其他的线程也看不到这个改动吗?
我觉得对于分配在堆上的对象,JVM不可能会为每一个线程都deep copy一份。所以,每个线程引用到的都是堆上的那个对象。
有些人说,CPU会在自己的缓存里缓存add方法调用的CPU指令,而不是立即刷出到主存里。如果是这样的话,对象的方法调用确实会存在不可见问题,但我在自己机器上测试,并没有出现不可见问题。按理说,多核心CPU的设计不应该这么弱智,这种情况只可能会出现在多CPU情况下。

疑惑中

问题补充:
mercyblitz 写道


之所有你可能看到变化,是因为Java会保持最终一致性,线程修改的内容(在Cache或寄存器中)最终会提交(同步)内存中。


引用
原则上应该是都可见,但我个人理解是可见不能就能访问(读和写)。



可见是说能不能看到其他线程的修改后内容,读写和可见无关,无论可见与否,读写都是可以的。


这是Java Language Specification里的:
17.4.1 Shared Variables
Memory that can be shared between threads is called shared memory or heap memory.

所以,关于这个问题,如果不可见,也只可能是CPU缓存缓存了操作指令。JVM copy 对象是不可能的,那样的话效率太低了。

但你说的java最终会保持一致性,这个有点模糊。如果是thread stack里的数据,比如原生类型。一定会在stack出栈的时候才会刷出到主存。但这是JVM所控制的。我举的例子,似乎超出了JVM的控制范围。刷出CPU缓存数据到主存应该是由CPU自身的控制器所决定的。。。

问题补充:如果CPU缓存里放了在heap里的对象,那JSP里的:Memory that can be shared between threads is called shared memory or heap memory.这句话就有点问题了,内存数据不再是共享的状态。

看一下这里:
http://www.artima.com/insidejvm/ed2/jvm2.html
有一句话:
A thread's Java stack stores the state of Java (not native) method invocations for the thread. The state of a Java method invocation includes its local variables, the parameters with which it was invoked, its return value (if any), and intermediate calculations.
似乎线程并不能copy在堆上的对象。我说效率低,是因为如果堆上的对象实际就是程序的领域对象了。这个对象集合可能非常大。比如一个graph数据结构,起内部的点边是相互关联的,线程遍历这个graph,修改每一个节点每一条边,如果依次copy对象到CPU缓存,效率是不可接受的。copy一个也许是OK的

所以,关于这个机制,是否有一些文档供参考?

问题补充:
mercyblitz 写道
引用

如果CPU缓存里放了在heap里的对象,那JSP里的:Memory that can be shared between threads is called shared memory or heap memory.这句话就有点问题了,内存数据不再是共享的状态。

看一下这里:
http://www.artima.com/insidejvm/ed2/jvm2.html
有一句话:
A thread's Java stack stores the state of Java (not native) method invocations for the thread. The state of a Java method invocation includes its local variables, the parameters with which it was invoked, its return value (if any), and intermediate calculations.
似乎线程并不能copy在堆上的对象。我说效率低,是因为如果堆上的对象实际就是程序的领域对象了。这个对象集合可能非常大。比如一个graph数据结构,起内部的点边是相互关联的,线程遍历这个graph,修改每一个节点每一条边,如果依次copy对象到CPU缓存,效率是不可接受的。copy一个也许是OK的



JVM是由线程触发(启动)的,栈里面的内容是不会出现多线程安全的问题,栈里面的内容相当于ThreadLocal,OS线程利用CPU,关键是CPU会把内存中的数据Copy到Cache或寄存器中,如果是多核CPU中,多个线程分布在不同的核上,Cache或寄存器上就相互独立,最后提交到内存中,可能数据出现了不一致。

一般的对象是Cache足够了,大对象是有小对象组成的(或称为数据),二级缓存现在都在2MB以上,已经足够,至少可以缓存部分数据,其他部分再想内存获取,一般情况CPU会利用Cache或寄存器,而不会直接走内存,除非特殊指令控制以外,你去看看自己主板的前端总线的传输效率,常识,CPU的缓存比内存快很多很多!




恩,好像你对底层的东西很熟悉啊。
我在stackoverflow提了这个问题之后,发现这个问题本身走的太远了。因为按照正常的流程,add一定是会锁住的,所以这个问题就不再是个问题。至于JVM内部怎么运作,并没有文档可供查阅。
所有的人都告诉我,不能保证可见性。但内部运作方式是怎样的,这个可能走的有点太深了。

感谢
2011年7月18日 18:35

4个答案 按时间排序 按投票排序

0 0

采纳的答案

引用

如果CPU缓存里放了在heap里的对象,那JSP里的:Memory that can be shared between threads is called shared memory or heap memory.这句话就有点问题了,内存数据不再是共享的状态。

看一下这里:
http://www.artima.com/insidejvm/ed2/jvm2.html
有一句话:
A thread's Java stack stores the state of Java (not native) method invocations for the thread. The state of a Java method invocation includes its local variables, the parameters with which it was invoked, its return value (if any), and intermediate calculations.
似乎线程并不能copy在堆上的对象。我说效率低,是因为如果堆上的对象实际就是程序的领域对象了。这个对象集合可能非常大。比如一个graph数据结构,起内部的点边是相互关联的,线程遍历这个graph,修改每一个节点每一条边,如果依次copy对象到CPU缓存,效率是不可接受的。copy一个也许是OK的



JVM是由线程触发(启动)的,栈里面的内容是不会出现多线程安全的问题,栈里面的内容相当于ThreadLocal,OS线程利用CPU,关键是CPU会把内存中的数据Copy到Cache或寄存器中,如果是多核CPU中,多个线程分布在不同的核上,Cache或寄存器上就相互独立,最后提交到内存中,可能数据出现了不一致。

一般的对象是Cache足够了,大对象是有小对象组成的(或称为数据),二级缓存现在都在2MB以上,已经足够,至少可以缓存部分数据,其他部分再想内存获取,一般情况CPU会利用Cache或寄存器,而不会直接走内存,除非特殊指令控制以外,你去看看自己主板的前端总线的传输效率,常识,CPU的缓存比内存快很多很多!



2011年7月19日 12:36
0 0

引用

这是Java Language Specification里的:
17.4.1 Shared Variables
Memory that can be shared between threads is called shared memory or heap memory.

所以,关于这个问题,如果不可见,也只可能是CPU缓存缓存了操作指令。JVM copy 对象是不可能的,那样的话效率太低了。


效率低?不解啊,CPU会从内存中Copy数据的snapshot到cache或集群器中啊,JVM的内存模型是基于OS Thread模型和硬件架构汇编指令的。

引用

但你说的java最终会保持一致性,这个有点模糊。如果是thread stack里的数据,比如原生类型。一定会在stack出栈的时候才会刷出到主存。但这是JVM所控制的。我举的例子,似乎超出了JVM的控制范围。刷出CPU缓存数据到主存应该是由CPU自身的控制器所决定的。。。



线程Stack保存时临时变量,只是暂时Copy数据,Stack和Heap数据是两码事,并且Heap数据不一定会及时提交到主存中区。JVM会有最终一致性的实现,会让数据在某个时刻提交中主存,你你本机的测试是在并发压力不大情况下,如果压力大时,不会马上提交到内存,CPU到内存之间的总线传输开销是存在的。线程会利用CPU寄存器或N级缓存中的内容,而不是直接交互内存。

2011年7月19日 11:03
0 0



之所有你可能看到变化,是因为Java会保持最终一致性,线程修改的内容(在Cache或寄存器中)最终会提交(同步)内存中。


引用
原则上应该是都可见,但我个人理解是可见不能就能访问(读和写)。



可见是说能不能看到其他线程的修改后内容,读写和可见无关,无论可见与否,读写都是可以的。

2011年7月19日 10:12
0 0

原则上应该是都可见,但我个人理解是可见不能就能访问(读和写)。

2011年7月18日 21:35

相关推荐

    java多线程安全性基础介绍.pptx

    java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...

    JAVA多线程与并发学习总结.pdf

    本文总结了JAVA多线程与并发的相关知识点,涵盖计算机系统的高速缓存机制、JAVA内存模型、内存间交互操作、volatile型变量、原子性、可见性与有序性、先行发生原则等内容。 计算机系统使用高速缓存来作为内存与...

    Java多线程-知识点梳理和总结-超详细-面试知识点.docx

    volatile关键字是Java中的一种线程同步机制,用于解决多线程环境下的可见性和有序性问题。volatile关键字可以确保变量的修改对所有线程可见,并且可以禁止编译器和处理器对变量的优化。 Java内存模型 Java内存模型...

    Java学习源码Java多线程的代码

    在char01包里放置Java多线程基本知识的代码。内容如下: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 在char02包里放置了Java对变量和对象并发访问的知识的代码...

    java并发理论基础、可见性、原子性、有序性详解

    本资源涵盖了Java并发编程的理论基础和实践,主要包括可见性、原子性和有序性的详细介绍,以及多线程的使用原因、好处和坏处等方面...我们还将讲解Java多线程开发的好处和坏处,并探讨如何尽量减少由多线程带来的问题。

    详解java多线程的同步控制

    同步控制是并发程序必不可少的重要手段,本文我们将通过重入锁、读写锁、信号量、倒计数器和循环...保证了共享内存的原子性、可见性、有序性 JMM原理图 线程在访问主内存中的变量时并不是直接对主存中的变量进行读写,

    Java线程面试题Top50[参照].pdf

    * 在 Java 并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的 * volatile 变量可以保证下一个读取操作会在前一个写操作之后发生 十、什么是线程安全? * 线程安全是指在多线程环境下,某个类...

    从volatile说到i++的线程安全问题.docx

    本文主要讨论了volatile关键字在多线程环境下的应用,特别是解决了多线程间共享变量的可见性问题,以及i++操作的线程安全问题。 一、volatile关键字的作用 volatile关键字保证了两点: 1. 可见性:在多线程环境下...

    Java同步与异步.pdf

    Java同步与异步是Java编程中非常重要的概念,它们都是为了解决多线程环境中的线程安全问题。在多线程环境中,如果没有正确的同步机制,多个线程可能会同时访问共享资源,导致数据不一致和其他严重问题。 同步是指在...

    并发编程基础知识,java内存模型及多线程、volatile

    ● JMM的关键技术点都是围绕着多线程的原⼦性、可⻅性和有序性来创建的。所以,下⾯我们来⼀⼀ 介绍这三种特性。原子性、可见性、有序性。 正常情况下,如果我们不使⽤volatile,那么每条线程都会有⾃⼰的缓存,当...

    2020面试题总结多线程篇.pdf

    * volatile 可以保证可见性,当一个变量被 volatile 修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会自己被更新到主内存中,当其他线程读取共享变量时,他会直接从主内存中读取。 * volatile 可以...

    Java的线程安全内存模型.docx

    什么是线程安全问题? 当 多个线程 同时共享同一个 全局变量 ...o可见性:多个线程访问同一变量,一个线程修改了变量的值,其他线程可以立即看到修改的值 o有序性:程序按照代码的顺序先后执行(与指令重排有关)

    Java同步与异步[定义].pdf

    Java 同步与异步是一种编程技术,旨在解决多线程环境下的数据一致性和可见性问题。下面是 Java 同步与异步的定义和概念: 关键字: * thread(线程):Java 中的基本执行单元,能够独立执行一系列任务。 * thread...

    java内存屏障与JVM并发详解实用.pdf

    内存屏障的重要性在于,它可以解决多线程程序中的一些问题,如可见性、原子性和顺序性问题。当多个线程访问共享资源时,内存屏障可以确保这些线程按照正确的顺序执行操作,从而避免了数据不一致和线程安全问题。 三...

    线程安全在Java中的应用与实践.pptx.pptx

    volatile关键字可以保证变量的可见性,当一个线程修改了volatile变量的值,新值对于其他线程来说是立即可见的,这样可以防止由于线程间的数据不一致导致的问题 Lock接口 Lock接口提供了比synchronized关键字更广泛的...

    Test2.java

    java多线程的通讯共享数据就是共享内存,共享内存主要有可见性和有序原子性。java 内存原型JMM解决可见有序,线程锁synchronized所解决它的原子性。 JMM内存原型:java可以在不同操作系统不同硬件存储逻辑下运行的...

    Java并发:volatile内存可见性和指令重排

     Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存(比如CPU的寄存器),线程只能访问自己的工作内存,不可以访问其它线程的工作内存。  工作内存中保存了主内存...

    Java并发编程.docx

     缓存 可导致可见性问题。 o原子性 :一个或多个CPU执行操作不被中断。 线程切换 可导致原子性问题。 o有序性 :编译器优化可能导致指令顺序发生改变。 编译器优化 可能导致有序性问题。 三个问题 o安全性...

    Java并发面试题整理(答案)

    可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。 (3)有序性 有序性,即程序的执行顺序按照代码的先后顺序来执行。 2、实现可见性的方法有哪些? ...

    java并发编程实践笔记资料.pdf

    5. 不要将共享变量裸露在多线程环境下,应该使用同步或不可变性保护。 6. 在多线程环境下,延迟加载需要同步的保护,以避免对象重复实例化。 7. 对于volatile声明的数值类型变量进行运算,往往是不安全的,因为...

Global site tag (gtag.js) - Google Analytics