为什么局部变量在Java中是线程安全的

6tqwzwtp  于 2022-10-01  发布在  Java
关注(0)|答案(8)|浏览(213)

我在用Java阅读多线程时遇到了这样的情况
局部变量在Java中是线程安全的。

从那时起,我一直在思考局部变量如何/为什么是线程安全的。

有没有人能告诉我。

qrjkbowd

qrjkbowd1#

当您创建线程时,它将创建自己的call stack。两个线程将有两个堆栈,其中一个线程永远不会与其他线程共享其堆栈。

程序中定义的所有局部变量都将在堆栈中分配内存(正如Jatin所说,内存在这里指的是对象的引用值和基本类型的值)(线程调用的每个方法都在自己的堆栈上创建一个堆栈帧)。一旦该线程完成方法执行,堆栈帧就会被移除。

Stanford professor in youtube有一个很棒的讲座,可以帮助你理解这个概念。

dzjeubhm

dzjeubhm2#

局部变量存储在每个线程自己的堆栈中。这意味着局部变量永远不会在线程之间共享。这也意味着所有局部原语变量都是线程安全的。

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

对对象的本地引用略有不同。引用本身不是共享的。但是,引用的对象并不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。如果本地创建的对象从不转义在其中创建它的方法,则它是线程安全的。事实上,您也可以将其传递给其他方法和对象,只要这些方法或对象中没有一个使传递的对象对其他线程可用

mjqavswn

mjqavswn3#

想一想像定义功能这样的方法。当两个线程运行相同的方法时,它们完全没有关联。它们将各自创建各自版本的每个局部变量,并且将无法以任何方式相互交互。

如果变量不是局部的(就像在类级别的方法外部定义的示例变量),那么它们被附加到示例(而不是方法的一次运行)。在本例中,运行相同方法的两个线程都看到一个变量,这不是线程安全的。

请考虑以下两种情况:

public class NotThreadsafe {
    int x = 0;
    public int incrementX() {
        x++;
        return x;
    }
}

public class Threadsafe {
    public int getTwoTimesTwo() {
        int x = 1;
        x++;
        return x*x;
    }
}

在第一个示例中,在NotThreadsafe的同一示例上运行的两个线程将看到相同的x。这可能很危险,因为这两个线程正在尝试更改x!在第二种情况下,在Threadsafe的同一示例上运行的两个线程将看到完全不同的变量,并且不能相互影响。

nbnkbykc

nbnkbykc4#

每个方法调用都有自己的局部变量,显然,方法调用发生在单个线程中。仅由单个线程更新的变量本质上是线程安全的。

然而,请密切关注这到底是什么意思:只有对变量本身的写入是线程安全的;对它所引用的对象调用方法本身并不是线程安全的**。直接更新对象的变量也是如此。

r3i60tvu

r3i60tvu5#

除了南巴里的其他答案之外。

我想指出的是,您可以在无源类型方法中使用局部变量:

此方法可以在其他线程中调用,这可能会危及线程安全性,因此Java强制将无源类型中使用的所有局部变量声明为最终变量。

考虑一下这个非法代码:

public void nonCompilableMethod() {
    int i=0;
    for(int t=0; t<100; t++)
    {
      new Thread(new Runnable() {
                    public void run() {
                      i++; //compile error, i must be final:
                      //Cannot refer to a non-final variable i inside an
                      //inner class defined in a different method
                    }
       }).start();
     }
  }

如果Java确实允许这样做(就像C#通过“闭包”所做的那样),那么局部变量在所有情况下都不再是ThreadSafe。在这种情况下,不能保证所有线程末尾的i的值为100

oipij1gg

oipij1gg6#

线程将拥有自己的堆栈。两个线程将有两个堆栈,其中一个线程永远不会与其他线程共享其堆栈。局部变量存储在每个线程自己的堆栈中。这意味着局部变量永远不会在线程之间共享。

eh57zj3b

eh57zj3b7#

基本上,Java中有四种类型的存储来存储类信息和数据:

方法区、堆、Java堆栈、PC

所以方法区域和堆是由所有线程共享的,但是每个线程都有自己的Java堆栈和PC,并且不被任何其他线程共享。

Java中的每个方法都是作为Stack Frame的。因此,当一个线程调用一个方法时,该堆栈帧被加载到它的Java堆栈上。该堆栈帧和相关操作数堆栈中的所有局部变量都不会被其他线程共享。PC将在方法的字节代码中包含要执行的下一条指令的信息。所以所有的局部变量都是线程安全的。

@Weston也给出了很好的答案。

6xfqseft

6xfqseft8#

局部变量的Java线程安全

只有*局部变量存储在线程堆栈上。

局部变量,即primitive type(如int、long...)存储在thread stack上,因此其他线程无法访问它。

局部变量,即reference type(Object的后继变量)包含两个部分-地址(存储在thread stack上)和对象(存储在heap上)

class MyRunnable implements Runnable() {
    public void run() {
        method1();
    }

    void method1() {
        int intPrimitive = 1;

        method2();
    }

    void method2() {
        MyObject1 myObject1 = new MyObject1();
    }
}

class MyObject1 {
    MyObject2 myObject2 = new MyObject2();
}

class MyObject2 {
    MyObject3 myObject3 = MyObject3.shared;
}

class MyObject3 {
    static MyObject3 shared = new MyObject3();

    boolean b = false;
}

JVM Memory model(https://stackoverflow.com/a/65382087/4770877)

相关问题