线程之间是否共享静态variables?

我在一个高级Java线程课程的老师说了一些我不确定的东西。

他表示下面的代码不一定会更新readyvariables。 据他介绍,这两个线程不一定共享静态variables,特别是在每个线程(主线程与ReaderThread)在其自己的处理器上运行并且因此不共享相同的寄存器/caching/等等的情况下CPU不会更新其他。

基本上,他说有可能在主线程中更新了ready ,而不是在ReaderThread中,所以ReaderThread将无限循环。 他还声称,程序可以打印“0”或“42”。 我明白'42'是怎么打印的,但不是'0'。 他提到这是numbervariables设置为默认值时的情况。

我想也许不能保证在线程之间更新静态variables,但这让我觉得Java非常奇怪。 ready挥霍纠正这个问题吗?

他展示了这个代码:

  public class NoVisibility {  
    私有静态布尔准备好;  
    私人静态诠释数字;  
    私有静态类ReaderThread扩展Thread {   
         public void run(){  
             while(!ready)Thread.yield();  
            的System.out.println(数);  
         }  
     }  
     public static void main(String [] args){  
         new ReaderThread()。start();  
        数字= 42;  
         ready = true;  
     }  
 } 

没有特定于静态variables的可见性问题。 JVM的内存模型有一个可见性问题。 这里有一篇关于内存模型的文章,以及如何让线程可见的写入 。 你不能指望一个线程及时地对其他线程可见的更改(实际上JVM没有义务让这些更改对你完全可见),除非你build立了一个事前关系 ,下面是一个引用那个链接(在Jed Wesley-Smith的评论中提供):

Java语言规范的第17章定义了内存操作(如读取和写入共享variables)上的发生前关系。 只有在写入操作发生时,一个线程的写入结果才能保证对另一个线程的读取可见 – 在读取操作之前。 synchronized和volatile结构,以及Thread.start()和Thread.join()方法都可以形成事前关系。 尤其是:

  • 线程中的每个动作都会在该线程中的每个动作之前发生,而这些动作在程序的顺序中稍后。

  • 监视器的解锁(同步块或方法退出)发生在相同监视器的每个后续locking(同步块或方法input)之前。 而且由于发生之前的关系是可传递的,所以在解锁之前的线程的所有动作发生在任何线程locking该监视器之后的所有动作之前。

  • 写一个volatile字段发生在每个后续的同一个字段的读取之前。 写入和读取易失性字段具有与进入和退出显示器类似的内存一致性效果,但不需要互斥locking。

  • 在启动的线程中执行任何操作之前,都会调用线程启动。

  • 线程中的所有操作都会在任何其他线程成功从该线程上的连接返回之前发生。

他在谈论能见度,而不是太字面意思。

静态variables实际上是在线程之间共享的,但是在一个线程中所做的更改可能不会立即被另一个线程看到,看上去好像有两个variables副本。

本文提供了一个与他如何呈现信息一致的视图:

首先,您必须了解一些有关Java内存模型的内容。 多年来我一直在努力解释这个问题。 就目前而言,我能想到的最好的方式就是用这种方式来想象:

  • Java中的每个线程发生在一个单独的内存空间(这显然是不真实的,所以忍受这一个)。

  • 您需要使用特殊的机制来保证这些线程之间的通信,就像在消息传递系统上一样。

  • 内存写入发生在一个线程中可以“泄漏”,并被另一个线程看到,但这并不是保证。 没有明确的沟通,你不能保证哪些写入被其他线程看到,甚至不能看到他们看到的顺序。

线程模型

但是,这只是一个思考线程和变化的思维模型,而不是字面上的JVM如何工作。

基本上这是真的,但实际上这个问题更复杂。 共享数据的可见性不仅受CPU高速caching的影响,还受到指令无序执行的影响。

因此,Java定义了一个内存模型 ,它指出线程在哪种情况下可以看到共享数据的一致状态。

在您的具体情况下,添加volatile保证能见度。

在一个类加载器中,静态字段总是被共享。 要显式地将数据范围限定到线程,你需要使用像ThreadLocal这样的工具。

当然,它们是“共享”的,即它们都指向相同的variables,但是它们不一定看到彼此的更新。 对于任何variables都是如此,而不仅仅是静态的。

理论上,由另一个线程写入的写入可能会以不同的顺序出现,除非variables被声明为volatile或写入被显式同步。

@dontocsata你可以回到你的老师和他的学校一点:)

来自现实世界的笔记,不pipe你看到或被告知。 请注意,下面的文字是关于这个特定的情况下显示的确切顺序。

以下2个variables将在几乎任何已知的体系结构下驻留在同一个caching行上。

 private static boolean ready; private static int number; 

Thread.exit (主线程)保证退出和exit是保证导致内存围墙,由于线程组线程删除(和许多其他问题)。 (这是一个同步调用,而且我看不到没有同步部分实现的单一方法,因为如果没有守护进程线程,ThreadGroup必须终止,等等)。

启动线程ReaderThread将保持进程活着,因为它不是一个守护进程! 因此, readynumber将被刷新在一起(或者如果上下文切换发生之前的数字),在这种情况下没有真正的重新sorting理由,至less我甚至不能想到一个。 你会需要一些真正奇怪的东西,除了42 。 我再次假设这两个静态variables将在同一个caching行中。 我无法想象一个4字节长的caching行,或者一个JVM,它们不会在连续区域(caching行)中分配它们。

Interesting Posts