Javaでローカル変数がスレッドセーフである理由


90

私はJavaでマルチスレッドを読んでいて、これに遭遇しました

ローカル変数はJavaではスレッドセーフです。

それ以来、ローカル変数がスレッドセーフである理由と理由を考えていました。

誰か教えてください。


27
スタックに割り当てられているためです。そして、スレッドは、各...のために独自のシェアスタック..ない
のRohitジャイナ

回答:


103

スレッドを作成すると、独自のスタックが作成されます。2つのスレッドには2つのスタックがあり、1つのスレッドがそのスタックを他のスレッドと共有することはありません。

プログラムで定義されたすべてのローカル変数には、スタック内のメモリが割り当てられます(Jatinがコメントしたように、ここでのメモリは、オブジェクトの参照値とプリミティブ型の値を意味します)(スレッドによる各メソッド呼び出しは、独自のスタックにスタックフレームを作成します)。このスレッドによってメソッドの実行が完了するとすぐに、スタックフレームが削除されます。

この概念を理解するのに役立つかもしれないスタンフォード教授によるyoutubeの素晴らしい講義があります。


13
申し訳ありませんが、あなたは間違っています。プリミティブなローカル変数のみがスタックに格納されます。残りのすべての変数はヒープに格納されます。Java 7ではエスケープ分析が導入され、一部の変数ではスタックに割り当てられる可能性があります
ジャティン

6
スタックは、ヒープ上のオブジェクトへの参照のみを保持します。スタックがクリアされるため、参照もクリアされます。したがって、ガベージコレクションに利用できます
ジャティン2013

6
@ジャティン:あなたは正しいです。メモリを意味するときは、オブジェクトの参照値とプリミティブの値を意味します(初心者の開発者もオブジェクトがヒープ上にあることを知っていると思います)。
kosa

2
@Nambariただし、参照値が共有変数を指している場合。では、スレッドセーフであると言えるのでしょうか。
H.Rabiee 2014年

3
@hajder:変数を共有しているものは何ですか?そこから始めます。インスタンス変数とクラス変数のどちらが正しいですか?ローカル変数ではなく、このスレッドでMarko Toplinkの回答を読んでください。それが、混乱している点だと思います。
kosa 14年

19

ローカル変数は、各スレッドの独自のスタックに格納されます。つまり、ローカル変数がスレッド間で共有されることはありません。これは、すべてのローカルプリミティブ変数がスレッドセーフであることも意味します。

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

オブジェクトへのローカル参照は少し異なります。参照自体は共有されません。ただし、参照されるオブジェクトは、各スレッドのローカルスタックには保存されません。すべてのオブジェクトは共有ヒープに格納されます。ローカルで作成されたオブジェクトが、それが作成されたメソッドをエスケープしない場合、それはスレッドセーフです。実際には、これらのメソッドまたはオブジェクトのいずれも、渡されたオブジェクトを他のスレッドで利用可能にしない限り、他のメソッドやオブジェクトに渡すこともできます。


agrumentに誤りがあります。@ Nambari応答のコメントを見てください
Jatin

localSafeIntが常に0であるという事実を指している場合は、1にしてから削除します。したがって、この変数はスレッド間で共有されないため、マルチスレッドの影響を受けないことを示しています。スレッドセーフは常に0または1に過ぎないことをもう少し指摘できます
tObi

14

機能の定義のようなメソッドを考えてください。2つのスレッドが同じメソッドを実行する場合、それらはまったく関連していません。それらはそれぞれ各ローカル変数の独自のバージョンを作成し、いかなる方法でも相互に対話することができません。

変数がローカルでない場合(クラスレベルでメソッドの外部で定義されたインスタンス変数など)、変数はインスタンスにアタッチされます(メソッドの1回の実行ではありません)。この場合、同じメソッドを実行する2つのスレッドはどちらも1つの変数を参照しますが、これはスレッドセーフではありません。

次の2つのケースを検討してください。

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は、の同じインスタンスで実行されている2つのスレッドは同じxを参照します。スレッドがxを変更しようとしているため、これは危険な場合があります。2番目の例でThreadsafeは、の同じインスタンスで実行されている2つのスレッドは、まったく異なる変数を参照するため、互いに影響を与えることはできません。


6

各メソッドの呼び出しには独自のローカル変数があり、明らかに、メソッドの呼び出しは単一のスレッドで行われます。単一のスレッドによってのみ更新される変数は、本質的にスレッドセーフです。

ただし、これが何を意味するかに注意してください。変数自体への書き込みのみがスレッドセーフです。それが参照するオブジェクトのメソッドの呼び出しは、本質的にスレッドセーフではありません。オブジェクトの変数を直接更新する場合も同様です。


1
「それが参照するオブジェクトのメソッドの呼び出しは、本質的にスレッドセーフではない」と言います。しかし、このメソッドスコープでインスタンス化された、メソッドローカル参照によって参照されるオブジェクトは、2つのスレッドでどのように共有できますか?例で指摘してもらえますか?
Akshay Lokur 2014年

1
ローカル変数は、メソッドスコープ内でインスタンス化されたオブジェクトを保持する場合と保持しない場合がありますが、問題の一部ではありませんでした。たとえそうであっても、メソッドは共有状態にアクセスする場合があります。
Marko Topolnik 2014年

6

なんばりなどの他の回答に加えて。

匿名型メソッドでローカル変数を使用できることを指摘しておきます。

このメソッドは、スレッドの安全性を損なう可能性のある他のスレッドで呼び出される可能性があるため、Javaは匿名型で使用されるすべてのローカル変数をfinalとして強制的に宣言します。

この違法なコードを考えてみましょう:

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#が「クロージャ」を介して行うように)、ローカル変数はすべての状況でスレッドセーフではなくなります。この場合、iすべてのスレッドの最後のの値がであるとは限りません100


こんにちはウェストン、上記の議論と以下の回答から、私はjavaがすべてのローカル変数のスレッドセーフを保証することを理解しました。では、同期されたキーワードの実際の用途は何でしょうか?このような例で説明していただけませんか。
プラブー

5

スレッドは独自のスタックを持ちます。2つのスレッドには2つのスタックがあり、1つのスレッドがそのスタックを他のスレッドと共有することはありません。ローカル変数は、各スレッドの独自のスタックに格納されます。つまり、ローカル変数がスレッド間で共有されることはありません。


3

基本的に、Javaにはクラス情報とデータを格納するための4つのタイプのストレージがあります。

メソッド領域、ヒープ、JAVAスタック、PC

そのため、メソッド領域とヒープはすべてのスレッドで共有されますが、すべてのスレッドは独自のJAVAスタックとPCを持ち、それは他のスレッドでは共有されません。

Javaの各メソッドはStackフレームです。そのため、1つのメソッドがスレッドによって呼び出されると、そのスタックフレームはそのJAVAスタックにロードされます。そのスタックフレームにあるすべてのローカル変数と関連するオペランドスタックは、他のものと共有されません。PCには、メソッドのバイトコードで実行する次の命令の情報があります。したがって、すべてのローカル変数はスレッドセーフです。

@Westonも良い答えを与えています。


1

ローカル変数のみがスレッドスタックに格納されます。

ローカル変数であるprimitive type(例えばint型、長い...)に格納されthread stack、結果として-他のスレッドがそれにアクセスすることはできません。

ローカル変数であるreference type(の後継は、Object(上に格納されたアドレス- )2つの部分から含有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;
}

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.