线程缓存和Java内存模型

时间:2013-12-12 19:54:43

标签: java memory-management garbage-collection

我正在尝试了解Java内存模型和线程。据我所知,每个线程都有一个“主”内存的本地副本。因此,如果一个线程试图更改某个对象的int变量,它会缓存int变量,如果它更改它,其他线程可能看不到更改。但是如果线程缓存某些对象而不是int呢?在这种情况下,哪些线程会缓存它?如果一个线程缓存对象的引用,对对象状态的任何更改都不会被其他线程看到?为什么呢?

提前谢谢

5 个答案:

答案 0 :(得分:24)

CPU具有不同的级别缓存L1,L2,L3。每个CPU(以及/或CPU核心)都有自己的缓存。此高速缓存存储最小的主存储器(RAM)以提高性能。

  _______________    ______________  
 |     CPU 1     |  |     CPU 2    |  
 |   _________   |  |   _________  |  
 |  | Level 1 |  |  |  | Level 1 | |  
 |  |   Cache |  |  |  |  Cache  | |  
 |  |         |  |  |  |         | |
 |  |_________|  |  |  |_________| |  
 |_______________|  |______________|
           | |              | |
           | |              | |
          _|_|______________|_|__
         |                       |
         |      MAIN MEMORY      | 
         |_______________________|


  Time     Command                 CPU 1 (Cache)      CPU 2 (Cache)        Main Memory     
-------  ----------              ----------------    --------------       -------------
  1          ---                       ---                ---                x = 10
  2       Read x  (on cpu1)           x = 10              ---                x = 10
  3       Write x <--20 (on cpu1)     x = 20              ---                x = 10       
  4       Read  x (on cpu2)           x = 20              x = 10             x = 10
  5       put cache to Main mem       x = 20              x = 10             x = 20

例如,在执行顺序上,CPU2上的x值是错误的。 x值已由CPU1更改。 如果x变量定义为volatile,则所有写操作立即反映到主存。

答案 1 :(得分:13)

线程没有内存的本地副本。线程读/写的部分内存可以来自缓存,而不是主内存。缓存不需要彼此同步,也不需要与主存储器同步。所以这就是你可以观察到不一致的地方。

  

因此,如果一个线程试图更改一个int变量,例如某个对象,它会缓存int变量,如果它改变了它,其他线程可能看不到更改。

这是正确的。 Java内存模型是在规则之前定义的,例如:在字段x的易失性写入和字段x的易失性读取之间的规则之前发生。因此,当写入完成后,后续读取将看到写入的值。

如果没有这样的事情发生在关系之前,所有的赌注都会被取消(同时指令重新排序会使规则之前没有发生时生活变得复杂)。

  

如果线程缓存对对象的引用,对对象状态的任何更改对其他线程也不可见?为什么呢?

它可能是可见的..它也可能不可见。所有投注都没有在规则之前发生。之所以如此,那么很多优化就像硬件技巧一样加快速度,或者不允许使用编译器技巧。当然,始终保持内存与缓存同步会降低性能。

答案 2 :(得分:13)

CPU有多个缓存。正是这些硬件缓存可能具有不一致的数据副本。它们可能不一致的原因是保持一致性会使代码速度降低10倍,并破坏从多线程中获得的任何好处。要获得不错的性能,您需要选择性地保持一致。 Java内存模型描述了它何时确保数据是一致的,但在最简单的情况下它不会。

注意:这不仅仅是CPU问题。可以在代码中内联不必在线程之间保持一致的字段。这可能意味着,如果一个线程更改了该值,则另一个线程可能永远不会看到此更改,因为它已被烧入代码中。

答案 3 :(得分:2)

“然而,在编写体面的多线程代码之前,您确实需要更多地研究多线程代码的复杂性和细微之处。

说到线程,保证很少。

你能想象当两个不同的线程可以访问一个类的单个实例时可能发生的破坏,两个线程都调用该对象上的方法......这些方法是否会修改对象的状态? ......甚至可视化也太可怕了。“,来自Sun认证程序员Java 6,第9章:主题。

我的朋友,

在Java中,线程不会缓存任何对象或变量,只是对象实例具有引用。谈论线程缓存内存更像是谈论操作系统线程 ...无论内部如何管理线程,Java都以相同的方式在所有操作系统中工作很大程度上取决于不同的操作系统。

看一下这段代码:

AccountDanger r = new AccountDanger();
Thread one = new Thread(r):
Thread two = new Thread(r);

如您所见,在这种情况下,线程可以访问同一个实例: r 。然后,您将遇到同步问题,当然......如果我们谈论本地成员或对象成员并不重要,则第一和第二个线程可以访问所有 r 的成员(如果他们通过范围或设置者/获取者可访问),他们将直接读取 r 实例中的值。即使你没有注意到它,这肯定是肯定的,有时候真的很难。

如果您想编写多线程应用程序,我建议您阅读 java范围 java同步

此致

答案 4 :(得分:-5)

每个线程都没有本地内存副本。如果变量对多个线程可见(因为它的范围),则每个线程都会看到相同的值。

但是,多线程程序需要非常小心共享变量(内存),因为如果你不小心,很容易引入竞争条件。