局部变量是否安全?

时间:2015-12-10 21:47:00

标签: java multithreading thread-safety

已经有类似的问题,但它没有回答以下问题。众所周知,字段的值不一定在线程之间立即同步。但这也是局部变量的情况吗?可以抛出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?");
    }
}

6 个答案:

答案 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)

局部变量存储在堆栈中而不是堆中,因此它们是线程安全的