私はJavaでマルチスレッドを読んでいましたが、これに出くわしました
Java では、ローカル変数はスレッドセーフです。
それ以来、ローカル変数がスレッドセーフである方法/理由について考えてきました。
誰か教えてください。
私はJavaでマルチスレッドを読んでいましたが、これに出くわしました
Java では、ローカル変数はスレッドセーフです。
それ以来、ローカル変数がスレッドセーフである方法/理由について考えてきました。
誰か教えてください。
スレッドを作成すると、独自のスタックが作成されます。2 つのスレッドが 2 つのスタックを持ち、1 つのスレッドがそのスタックを他のスレッドと共有することはありません。
プログラムで定義されたすべてのローカル変数には、スタック内のメモリが割り当てられます (Jatin がコメントしたように、ここでのメモリは、オブジェクトの参照値とプリミティブ型の値を意味します) (スレッドによる各メソッド呼び出しは、独自のスタックにスタック フレームを作成します)。このスレッドによるメソッドの実行が完了するとすぐに、スタック フレームは削除されます。
この概念を理解するのに役立つかもしれないYouTubeのスタンフォード教授による素晴らしい講義があります.
ローカル変数は、各スレッド独自のスタックに格納されます。つまり、ローカル変数がスレッド間で共有されることはありません。これは、すべてのローカル プリミティブ変数がスレッド セーフであることも意味します。
public void someMethod(){
long threadSafeInt = 0;
threadSafeInt++;
}
オブジェクトへのローカル参照は少し異なります。参照自体は共有されません。ただし、参照されるオブジェクトは、各スレッドのローカル スタックには格納されません。すべてのオブジェクトは共有ヒープに格納されます。ローカルで作成されたオブジェクトが作成されたメソッドを決してエスケープしない場合、そのオブジェクトはスレッド セーフです。実際、これらのメソッドまたはオブジェクトのいずれも、渡されたオブジェクトを他のスレッドで使用可能にしない限り、他のメソッドやオブジェクトに渡すこともできます。
メソッドは、機能の定義のようなものと考えてください。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;
}
}
最初の例では、 の同じインスタンスで実行されている 2 つのスレッドNotThreadsafe
が同じ x を参照します。スレッドが x を変更しようとしているため、これは危険な場合があります。2 番目の例では、 の同じインスタンスで実行されている 2 つのスレッドは、Threadsafe
まったく異なる変数を認識し、互いに影響を与えることはできません。
各メソッド呼び出しには独自のローカル変数があり、明らかに、メソッド呼び出しは単一のスレッドで発生します。単一のスレッドによってのみ更新される変数は、本質的にスレッドセーフです。
ただし、これが正確に何を意味するのかを注意深く監視してください。変数自体への書き込みのみがスレッドセーフです。参照するオブジェクトでメソッドを呼び出すことは、本質的にスレッドセーフではありません。オブジェクトの変数を直接更新する場合も同様です。
スレッドには独自のスタックがあります。2 つのスレッドが 2 つのスタックを持ち、1 つのスレッドがそのスタックを他のスレッドと共有することはありません。ローカル変数は、各スレッド独自のスタックに格納されます。つまり、ローカル変数がスレッド間で共有されることはありません。
ナンバリさんなどの他の回答に加えて。
無名型メソッドでローカル変数を使用できることを指摘したいと思います。
このメソッドは、スレッドセーフを損なう可能性のある他のスレッドで呼び出される可能性があるため、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には基本的に4つのタイプのストレージがあります。
メソッド領域、ヒープ、JAVA スタック、PC
したがって、メソッド領域とヒープはすべてのスレッドで共有されますが、すべてのスレッドには独自の Java スタックと PC があり、他のスレッドとは共有されません。
Java の各メソッドは Stack フレームです。そのため、1 つのメソッドがスレッドによって呼び出されると、そのスタック フレームがその Java スタックにロードされます。そのスタック フレームおよび関連するオペランド スタックにあるすべてのローカル変数は、他のものと共有されません。PC は、メソッドのバイト コードで実行する次の命令の情報を持っています。したがって、すべてのローカル変数は THREAD SAFE です。
@Westonも良い答えを与えています。