关于java内存模型中read和load操作的一个问题,急求大神指点

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于java内存模型中read和load操作的一个问题,急求大神指点相关的知识,希望对你有一定的参考价值。

阅读《java并发编程实战》这本书,其中对java内存模型中read和load操作额度定义如下:
read(读取): 作用于主内存中的变量,它把一个变量的值从主内存传输到线程的工作内存中, 以便随后的load动作使用。
load(载入):作用于工作内存的变量, 他把read操作从主内存中得到的变量值放入工作内存的变量副本中
对于以上定义,我有一些问题, read操作把一个变量的值从主内存传输到线程的工作内存中,那线程的工作内存中应该有一个位置来存放这个变量,
假设这个位置是A,接下来,load操作把从主内存中得到的变量值放入工作内存的变量副本中,也就是把A位置的值放入工作内存的变量副本中,这个所谓的变量副本也应该对应了一个存储位置,假设这个位置是B,如果这样理解的话,那对于一个变量,它在线程的工作内存就有两个位置??
但是在网上查阅资料,发现他们所说的都是一个变量在线程的工作内存中值对应了一个位置,请问这个read和load操作该怎么理解???

我这是么个理解。
所谓的“线程的工作内存”就是线程栈,这个栈大小是通过 vm参数 -xss 来设置的。不同版本的jdk对应的大小不一样,有的是512k。上面的read操作和load操作,我理解实际整体是在一个操作步骤中,所谓的read应是根据引用地址从堆中(即通过-Xms -Xmx 参数来设置的)读出来;然后load过程,就是在线程运算需要使用此变量时,将变量的具体值(即引用指向的内存地址存储的数据)压入线程栈。
这也就是为什么如果在并发时,同一个变量不加锁会导致结果不可预料的问题。因为不同线程运算时,都会将变量值压入当前线程栈而不是直接对堆上变更直接进行操作,这样就存在一个时间窗口会导致不同的线程对堆中同一个变量值的不同“副本”进行操作,从而导致结果不可预料,也就引用了并发冲突问题。追问

你把值从堆中读出来,读出来以后你把它放在哪?或者说暂存在哪?你下面也说了,在线程运算需要使用此变量时,才把值压入线程栈;我的理解是,如果读出来和压栈动作不是连续地进行,它就应该有个存储位置暂存读出来的值,就像软件工程中数据流和数据存储的关系一样

追答

就是存储在栈中,线程只能直接访问当前线程的线程栈。之所以要压栈,肯定是因为线程需要使用到这个变量了。

追问

追答

同学:貌似我没说read操作是把值存储在栈中吧。。

A read action (by the main memory) transmits the contents of
the master copy of a variable to a thread's working memory for use by a
later load operation.
read操作(主内存)是把主内存的一个变量内容(值)副本传递到线程的工作内存,从而为稍后的load操作使用。

A load action (by a thread) puts a value transmitted from main memory by a read action into the thread's working copy of a variable.
load操作(线程)把主内存中的一个值(即上面说的副本)传递到线程工作内存中的一个变量副本中。

上面的英文比较直白吧,只是用中文表达可能有点拗口。
VM规范关于这个细节的传送门:http://docs.oracle.com/javase/specs/jvms/se6/html/Threads.doc.html
==========补充====
理解楼主补充的意思了,VM规范并没有规定这个副本值在从主内存read出来后,到被load到线程中这个阶段放在哪,只是约定了这么一个过程。所以我个人认为应该是依赖不同的VM实现,例如sun有 sun的实现,oracle有oracle,bea有bea的实现,不过这三现在都一家了。IBM有IBM的实现。

追问

我想问的是这个中转位置是否存在?而不是“放在哪”。“放在哪”的前提是这个中转位置存在,现在连存在性问题都没有回答咧。已私信你了,求回复

参考技术A 个人理解,抛开工作内存看,read应该是读到cpu-cache里,然后load,读取cpu-cache到内存里是,分两步操作的,写也是同样道理 参考技术B read 读到高速缓存 lo a d 从高速缓存加载到 工作内存副本中(虚拟机栈)

Java 锁

1.volatile

volatile具有可见性和一致性,禁止指令重排序优化,但是无法保证原子性,因为很多操作都不是原子操作(如i++)

由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个

除了volatile之外,Java还有两个关键字能实现可见性,它们是synchronized和final。而final关键字的可见性是指:被final修饰的字段在构造器中一旦被初始化完 成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通 过这个引用访问到“初始化了一半”的对象),那么在其他线程中就能看见final字段的值。

由于光使用volatile和sychronized无法保证线程的绝对安全,因此引入了锁机制

2.ReentrantLock

ReentrantLock与synchronized相比增加了一些高级功能,主要有以下三项:等待可中断、可实现公 平锁及锁可以绑定多个条件。

Lock应该确保在finally块中释放锁,否则一旦受同步保护的代码块中抛出异常,则有可能永远不 会释放持有的锁。这一点必须由程序员自己来保证,而使用synchronized的话则可以由Java虚拟机来确 保即使出现异常,锁也能被自动释放。

以上是关于关于java内存模型中read和load操作的一个问题,急求大神指点的主要内容,如果未能解决你的问题,请参考以下文章

Java内存模型对并发处理的三个特性

JVM的内存模型

Java并发编程

深入理解Java内存模型(JMM)

Java中Volatile的理解

Java 并发编程线程简介 ( 原子操作 | volatile 关键字使用场景 )