私は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;
}
}
最初の例では、NotThreadsafe
の同じインスタンスで実行されている2つのスレッドは同じxを参照します。スレッドがxを変更しようとしているため、これは危険です。 2番目では、Threadsafe
の同じインスタンスで実行されている2つのスレッドはまったく異なる変数を参照し、相互に影響を与えることはできません。
なんばりのような他の答えに加えて。
匿名型メソッドでローカル変数を使用できることを指摘したいと思います。
このメソッドは、スレッドの安全性を損なう可能性のある他のスレッドで呼び出すことができるため、Javaは、anoymous型で使用されるすべてのローカル変数を強制的に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
であるとは限りません。
各メソッド呼び出しには独自のローカル変数があり、明らかに、メソッド呼び出しは単一のスレッドで発生します。単一のスレッドによってのみ更新される変数は、本質的にスレッドセーフです。
ただし、これが何を意味するのかを注意深く監視します。のみ変数自体への書き込みはスレッドセーフです。参照するオブジェクトのメソッドを呼び出す本質的にスレッドセーフではありません。オブジェクトの変数を直接更新する場合も同様です。
スレッドには独自のスタックがあります。 2つのスレッドには2つのスタックがあり、1つのスレッドが他のスレッドとスタックを共有することはありません。ローカル変数は、各スレッドの独自のスタックに保存されます。つまり、ローカル変数がスレッド間で共有されることはありません。
基本的に4つのタイプのストレージがありますJavaクラス情報とデータを格納するには:
メソッドエリア、ヒープ、Javaスタック、PC
そのため、メソッド領域とヒープはすべてのスレッドで共有されますが、すべてのスレッドは独自のJavaスタックとPCを持ち、他のスレッドでは共有されません。
Javaの各メソッドはStackフレームとして使用されます。そのため、1つのメソッドがスレッドによって呼び出されると、そのスタックフレームはJava Stack.Allそのスタックフレームにある変数と関連するオペランドスタックは他のユーザーと共有されません。PCはメソッドのバイトコードで実行する次の命令の情報を持っているため、すべてのローカル変数はTHREAD SAFEです。
@Westonも良い答えを出しています。
jenkov's の説明が好き
スレッドスタックには、実行される各メソッド(呼び出しスタック上のすべてのメソッド)に対してすべてのローカル変数が含まれます。スレッドは、自身のスレッドスタックにのみアクセスできます。スレッドによって作成されたローカル変数は、それを作成したスレッド以外のすべてのスレッドからは見えません。 2つのスレッドがまったく同じコードを実行している場合でも、2つのスレッドはそれぞれのスレッドスタックにそのコードのローカル変数を作成します。したがって、各スレッドには、各ローカル変数の独自のバージョンがあります。
プリミティブ型(ブール、バイト、short、char、int、long、float、double)のすべてのローカル変数は完全に格納されますスレッドスタック上であり、したがって表示されません他のスレッド。あるスレッドは、優先変数のコピーを別のスレッドに渡すことができますが、プリミティブローカル変数自体を共有することはできません。
ヒープを含むすべてのオブジェクト Javaアプリケーションで作成され、オブジェクトを作成したスレッドに関係なく、これにはプリミティブのオブジェクトバージョンが含まれます。タイプ(Byte、Integer、Longなど)オブジェクトが作成されてローカル変数に割り当てられたか、別のオブジェクトのメンバー変数として作成されたかは関係ありませんが、オブジェクトは引き続きヒープに格納されます。
ローカル変数はプリミティブ型の場合があり、その場合は完全に保持されますスレッドスタック上。
ローカル変数は、オブジェクトへのreferenceの場合もあります。その場合、参照(ローカル変数)はスレッドスタック上に格納されますが、格納される場合はオブジェクト自体ヒープ上に格納されます。
詳細をお読みください- http://tutorials.jenkov.com/Java-concurrency/Java-memory-model.html