Java 线程之间是否共享静态变量?

Java 线程之间是否共享静态变量?,java,multithreading,concurrency,static,memory-visibility,Java,Multithreading,Concurrency,Static,Memory Visibility,我的老师在一个高级Java线程课程上说了一些我不确定的话 他表示,以下代码不一定会更新ready变量。据他说,这两个线程不一定共享静态变量,特别是当每个线程(主线程与ReaderThread)在自己的处理器上运行时,因此不共享相同的寄存器/cache/etc,一个CPU不会更新另一个CPU 本质上,他说有可能在主线程中更新ready,但在ReaderThread中不更新,因此ReaderThread将无限循环 他还声称该程序可以打印0或42。我了解如何打印42,但不了解0。他提到,当number

我的老师在一个高级Java线程课程上说了一些我不确定的话

他表示,以下代码不一定会更新
ready
变量。据他说,这两个线程不一定共享静态变量,特别是当每个线程(主线程与
ReaderThread
)在自己的处理器上运行时,因此不共享相同的寄存器/cache/etc,一个CPU不会更新另一个CPU

本质上,他说有可能在主线程中更新
ready
,但在
ReaderThread
中不更新,因此
ReaderThread
将无限循环

他还声称该程序可以打印
0
42
。我了解如何打印
42
,但不了解
0
。他提到,当
number
变量设置为默认值时,会出现这种情况

我认为可能不能保证在线程之间更新静态变量,但这对Java来说非常奇怪。使
ready
volatile可以解决此问题吗

他展示了这个代码:

public class NoVisibility {  
    private static boolean ready;  
    private static int number;  
    private static class ReaderThread extends Thread {   
        public void run() {  
            while (!ready)   Thread.yield();  
            System.out.println(number);  
        }  
    }  
    public static void main(String[] args) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}

在单个类加载器中,静态字段总是共享的。要显式地将数据范围限定到线程,您需要使用类似于
ThreadLocal

的工具,静态变量在可见性方面没有任何特殊之处。如果它们是可访问的,任何线程都可以访问它们,因此您更可能看到并发问题,因为它们更容易暴露

JVM的内存模型带来了可见性问题。您不能指望一个线程所做的更改能够及时地对其他线程可见(实际上JVM没有义务在任何时间范围内使这些更改对您可见),除非您建立一个新的线程

以下是该链接的一段引用(由Jed Wesley Smith在评论中提供):

Java语言规范的第17章定义了内存操作(如读取和写入共享变量)上的“发生在之前”关系。只有在写操作发生在读操作之前,一个线程的写操作的结果才能保证对另一个线程的读操作可见。同步和易失性构造以及Thread.start()和Thread.join()方法可以在关系发生之前形成。特别是:

  • 线程中的每个动作都发生在该线程中的每个动作之前,该线程中的每个动作都是按照程序的顺序稍后出现的

  • 监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法进入)之前。而且因为“发生在之前”关系是可传递的,所以在解锁之前线程的所有操作都发生在监视的任何线程锁定之后的所有操作之前

  • 对易失性字段的写入发生在对该字段的每次后续读取之前。易失性字段的写入和读取与进入和退出监视器具有类似的内存一致性效果,但不需要互斥锁定

  • 对线程的启动调用发生在已启动线程中的任何操作之前

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


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

所以Java定义了一个状态,在这种情况下线程可以看到共享数据的一致状态

在您的特定情况下,添加
volatile
可以保证可见性。

它们当然是“共享”的,因为它们都引用相同的变量,但不一定能看到彼此的更新。这适用于任何变量,而不仅仅是静态变量

从理论上讲,由另一个线程进行的写操作可能会以不同的顺序出现,除非变量被声明为volatile,或者写操作是显式同步的。

他说的是可见性,不要太过直截了当

静态变量确实在线程之间共享,但在一个线程中所做的更改可能不会立即对另一个线程可见,这使得该变量似乎有两个副本

这篇文章提出了一个与他如何陈述信息一致的观点:

首先,您必须了解一些关于Java内存模型的知识。多年来,我一直在努力简明扼要地解释它。到今天为止,我能想到的最好的描述方式是,如果你这样想象:

  • Java中的每个线程都发生在一个单独的内存空间中(这显然是不真实的,所以请记住这一点)

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

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

但是,这只是一个考虑线程和易失性的思维模型,而不是JVM的实际工作方式。

@dontocsta 你可以回到你的老师那里,教他一点:)

很少有来自现实世界的笔记,不管你看到什么或听到什么。 请注意,下面的文字是按照所示的确切顺序与此特定案例相关的

在几乎任何已知的体系结构下,以下2个变量将驻留在同一缓存线上

private static boolean ready;  
private static int number;  
Thread.exit
(主线程)保证退出并退出
public static int i ;