私はJavaでマルチスレッドを読んでいて、これに遭遇しました
ローカル変数はJavaではスレッドセーフです。
それ以来、ローカル変数がスレッドセーフである理由と理由を考えていました。
誰か教えてください。
回答:
スレッドを作成すると、独自のスタックが作成されます。2つのスレッドには2つのスタックがあり、1つのスレッドがそのスタックを他のスレッドと共有することはありません。
プログラムで定義されたすべてのローカル変数には、スタック内のメモリが割り当てられます(Jatinがコメントしたように、ここでのメモリは、オブジェクトの参照値とプリミティブ型の値を意味します)(スレッドによる各メソッド呼び出しは、独自のスタックにスタックフレームを作成します)。このスレッドによってメソッドの実行が完了するとすぐに、スタックフレームが削除されます。
ローカル変数は、各スレッドの独自のスタックに格納されます。つまり、ローカル変数がスレッド間で共有されることはありません。これは、すべてのローカルプリミティブ変数がスレッドセーフであることも意味します。
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は匿名型で使用されるすべてのローカル変数を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つのスレッドがそのスタックを他のスレッドと共有することはありません。ローカル変数は、各スレッドの独自のスタックに格納されます。つまり、ローカル変数がスレッド間で共有されることはありません。
基本的に、Javaにはクラス情報とデータを格納するための4つのタイプのストレージがあります。
メソッド領域、ヒープ、JAVAスタック、PC
そのため、メソッド領域とヒープはすべてのスレッドで共有されますが、すべてのスレッドは独自のJAVAスタックとPCを持ち、それは他のスレッドでは共有されません。
Javaの各メソッドはStackフレームです。そのため、1つのメソッドがスレッドによって呼び出されると、そのスタックフレームはそのJAVAスタックにロードされます。そのスタックフレームにあるすべてのローカル変数と関連するオペランドスタックは、他のものと共有されません。PCには、メソッドのバイトコードで実行する次の命令の情報があります。したがって、すべてのローカル変数はスレッドセーフです。
@Westonも良い答えを与えています。
ローカル変数のみがスレッドスタックに格納されます。
ローカル変数である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;
}