已经有类似的问题,但它没有回答以下问题。众所周知,字段的值不一定在线程之间立即同步。但这也是局部变量的情况吗?可以抛出IllegalStateException吗?
public static void main(String[] args) {
final Thread mainThread = Thread.currentThread();
final Integer[] shared = new Integer[1];
new Thread(new Runnable() {
@Override
public void run() {
shared[0] = 1;
mainThread.interrupt();
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
if (shared[0] == null) throw new IllegalStateException("Is this possible?");
}
}
答案 0 :(得分:4)
实际上,shared
的值对于所有线程都是相同的。但是shared[0]
的值也涉及读取数组元素,并且该数组元素(如字段)可能会受到数据竞争的影响。
您确定
shared
安全吗?
是的,Java Language Specification写道:
局部变量(第14.4节),形式方法参数(第8.4.1节)和异常处理程序参数(第14.20节)永远不会在线程之间共享,并且不受内存模型的影响。
在JVM级别,每个线程都有自己的局部变量。如果匿名类访问封闭方法的局部变量,编译器会重写此代码以将变量的值作为构造函数参数传递给内部类,这将把它存储在最终字段中(这种重写是编译器需要的原因)这样的变量实际上是最终的并且明确赋值),并且通过访问最终字段来替换对该变量的所有访问。由于Java内存模型为最终字段提供special guarantees,即使通过数据竞争发布对象引用,此访问也是安全的,前提是此类发布仅在对象完成构建之后发生。
答案 1 :(得分:3)
局部变量是完全线程安全的,因为首先无法与另一个线程共享它们。
您的示例代码是完全不同的野兽,因为您实际上是在询问本地变量引用的共享数组的值。这是两件不同的事情。该变量是完全安全的(无论如何都不能改变,因为它的最终版本),它引用的数组的内容不会以任何方式同步,所以它也不安全。
编辑:详细说明名为“共享”的变量 当您将局部变量声明为final时,java允许您在所述变量的可见范围内定义的匿名类的范围内引用该变量(更简单:从定义变量的块内)。
看起来像一个变量,实际上是两个变量。您声明的那个存在于主线程中。创建匿名“new Runnable()”的那一刻,变量内容的副本(它实际上成为匿名类中隐藏的最终字段)。因此,当您在run() - 方法中引用“shared”时,您无法访问主线程中的本地变量“shared”。
您可以通过查看示例创建的类文件来验证这一点(有两个,一个用于类,一个用于匿名类),并使用javap -v来查看生成的字节代码。
答案 2 :(得分:0)
多个线程可见的局部变量不是线程安全的。必须通过常规机制(synchronized,volatile,immutable等)访问它们。
通常,您创建一个局部变量并在一个线程中使用它。准备好后,您必须Safely Publish该变量。在此之后,必须应用所有正常的线程安全机制。
答案 3 :(得分:0)
是的,局部变量是线程安全的,因为它们是在堆栈中分配的。但是,线程不共享堆栈。它们对于每个变量都是唯一的。
答案 4 :(得分:0)
shared是线程安全的,它引用的对象的状态是不安全的。
你的主线程可能会抛出异常,但极不可能。
告诉匿名线程start()并不一定意味着VM / OS实际上会在程序的下一部分执行之前启动你的线程。所以你的主线程可以在其他线程开始之前进入睡眠状态。如果在线程设置值之前它从睡眠中的外部事件中断,则最终可能为null。
主线程上的睡眠几乎可以确保匿名线程在共享测试之前运行。
考虑一下如果你删除了睡眠并在启动新线程后立即检查了null会发生什么。在我的系统上,共享[0]为空,大约50%的时间我运行你的程序修改以删除睡眠。
public static void main(String[] args) {
final Thread mainThread = Thread.currentThread();
final Integer[] shared = new Integer[1];
new Thread(new Runnable() {
public void run() {
shared[0] = 1;
mainThread.interrupt();
}
}).start();
if (shared[0] == null)
System.out.println("ouch");
}
答案 5 :(得分:0)
局部变量存储在堆栈中而不是堆中,因此它们是线程安全的