在路上

路上有惊慌,路上有理想

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  28 Posts :: 1 Stories :: 10 Comments :: 0 Trackbacks

Java 内存模型

JVM系统中存在一个主内存(Main Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

模型的规则:

1.原子性:保证程序得到成员变量(非局部变量)的值或者是初始值,又或者是某线程修改后的,绝对不是多个线程混乱修改后的。

2.可见性(共享内存的数据):什么情况下,写入成员变量的值对读取该变量的值是可见的?

     A.写操作释放了同步锁,读操作获得了同步锁

           原理:释放锁的时候强制线程把所使用的工作内存中的值刷新到主存,获得锁的时候从主存重新装载值。

           p.s.锁只被同步块和方法中的操作占有,但却控制了执行该操作的线程的所有成员变量。

     B.如果一个成员变量为volatile,那么在写线程做存储操作前,写入这个成员变量的数据会在主存中刷新,并对其他线程可见。读线程每次使用这个成员变量前都要重新从主存读数据。

     C.如果一个线程访问一个对象的成员变量,读到的值为初始值或者另一个线程修改后的值。

        p.s. 不要对引用未完全创建好的对象。

               如果一个类可以被子类化,那么在构造函数里启动一个线程是非常危险的

     D.当一个线程结束后,所有的写入数据都会被刷新到主存。

          p.s.同一个线程的不同方法之间传递对象的引用,永远不会有可见性问题

   存储模型保证:如果上面的操作都会发生,那么一个线程对一个成员变量的更新最终对另一个线程是可见的。

3.顺序化(内存操作的顺序):什么情况下,一个线程的操作可以是无序的?顺序化的问题主要围绕和读写有关的赋值语句的执行顺序。

   如果采用同步机制,那不用多说,顺序化可以保证。

   当没有同步机制时,存储模型所做的保证是难以相信的。在多线程环境下,存储模型是难以保证一定正确的。

  只有当满足下面的三个原则,顺序化才得以保证。

   A.从线程执行方法的角度看,如果指令都是串行执行的,那么顺序可以保证

   B.保证同步方法或块的顺序执行

   C.使用volatile定义成员变量

线程执行过程中,存储模型与锁的关系:

(1) 获取对象的锁

(2) 清空工作内存数据, 从主存复制变量到当前工作内存, 即同步数据

(3) 执行代码,改变共享变量值

(4) 将工作内存数据刷回主存

(5) 释放对象的锁

最后介绍一下volatile关键字

     volatile定义的成员变量可以保证可见性和顺序化,但不保证原子性。比如count++。
     *比如把一个变量声明为volatile,并不能保证这个变量引用的非volatile数据的可见性。比如volatile string[10](数组)

     正确使用volatile的前提条件

     a.对变量的写操作不依赖于当前值

     b.不要和其他成员变量遵守不变约束。见*处的解释

    volatile的应用

     a.状态标志

        volatile boolean shutdownFlag;

       public void shutdown() { shutdownFlag= true; }
       public void doWork() {
       while (!shutdownFlag) {
        // do something
         }

     b.假设一个后台线程可能会每隔几秒读取一次数据库里的合同金额,并更新至 volatile 变量。然后,其他线程可以读取这个变量,从而随时能够看到最新的金额。 比较广泛应用在统计类的系统中。

参考文档:

http://www.cs.umd.edu/~pugh/java/memoryModel/

http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

《Java并发编程:设计原则与模式》

posted on 2010-11-03 17:56 阮步兵 阅读(1434) 评论(0)  编辑  收藏 所属分类: Java

只有注册用户登录后才能发表评论。


网站导航: