与えられた:
public class TestSeven extends Thread {
private static int x;
public synchronized void doThings() {
int current = x;
current++;
x = current;
}
public void run() {
doThings();
}
}
どのステートメントが正しいですか?
A.コンパイルは失敗します。
B.実行時に例外がスローされます。
C. run()メソッドを同期すると、クラスはスレッドセーフになります。
D.変数「x」のデータは同時アクセスの問題から保護されています。
E. doThings()メソッドを静的として宣言すると、クラスはスレッドセーフになります。
F. synchronized(new Object()){}ブロックのdoThings()内のステートメントをラップすると、クラスはスレッドセーフになります。
そのクラスをスレッドセーフにするために、doThings()を同期済みとしてマークするだけでは十分ではありませんか?正解はDですが、この質問のモデルの答えはEですが、理由がわかりません。
E. doThings()メソッドを静的として宣言すると、クラスはスレッドセーフになります。
それはちょっとトリッキーな答えです。メソッドはすでに同期されていますが、インスタンス上にありますが、状態は静的フィールド、つまりクラスにあります。それを作るstatic synchronized
は確かに正解です。これは、(意味のない)インスタンスではなく、クラスで同期するためです。
D.変数「x」のデータは同時アクセスの問題から保護されています。
private static int x;
これは静的変数です。これはクラスのすべてのインスタンスで共有されるため、完全な使い捨てダミーオブジェクトで同期するFが役に立たないのと同じように、個々のインスタンスで同期することは役に立ちません。
言語仕様 によると:
同期メソッドは、実行前にモニター(§17.1)を取得します。
クラス(静的)メソッドの場合、メソッドのクラスのClassオブジェクトに関連付けられたモニターが使用されます。
インスタンスメソッドの場合、これに関連付けられたモニター(メソッドが呼び出されたオブジェクト)が使用されます。
これは、指定したコードでsynchronized
キーワードを指定すると、メソッドの本体を実行する前に、メソッドがthis
のロックを取得することを意味します。ただし、x
はstatic
であるため、x
への更新がアトミックになるとは限りません。 (クラスの別のインスタンスは、異なるthis
値を持ち、したがって異なるロックを持っているため、同期領域に入り、同時に更新を行うことができます。)
ただし、doStuff
staticを宣言すると、メソッドへのすべての呼び出しで同じロック(Class
のロック)が取得されるため、メソッド本体で相互排除が保証されます。
仕様は実際にそれを詳しく説明しています:
class A {
static synchronized void doSomething() {
// ...
}
}
文字通りと同じものです
class A {
static void doSomething() {
synchronized(A.class) {
// ...
}
}
}
同様に:
class B {
synchronized void doSomething() {
// ...
}
}
文字通りと同じものです
class B {
void doSomething() {
synchronized (this) {
// ...
}
}
}
DoThings()メソッドを同期することにより、特定のTestSevenオブジェクトのロックを保持しています。ただし、クラスの静的変数は、オブジェクト自体の特定のインスタンスに属していません。それらはClassオブジェクトに属しますTestSeven.class
。だから、あなたはどちらかのために行くことができます
synchronized (TestSeven.class){
int current = x;
current++;
x = current;
}
やり過ぎているインスタンスロック内のクラスロックを取得しているdoThings()メソッド内。したがって、メソッドを静的としてマークして、Classオブジェクトのロックのみを取得することができます。
x
はstatic
であるため、doThings
メソッドの実行と同時に他のスレッドがそれを変更する可能性があります。 doThings
static
を作成すると、これが停止します。