web-dev-qa-db-ja.com

クラスをスレッドセーフにする

与えられた:

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ですが、理由がわかりません。

14
Java Player

E. doThings()メソッドを静的として宣言すると、クラスはスレッドセーフになります。

それはちょっとトリッキーな答えです。メソッドはすでに同期されていますが、インスタンス上にありますが、状態は静的フィールド、つまりクラスにあります。それを作るstatic synchronizedは確かに正解です。これは、(意味のない)インスタンスではなく、クラスで同期するためです。

D.変数「x」のデータは同時アクセスの問題から保護されています。

private static int x;

これは静的変数です。これはクラスのすべてのインスタンスで共有されるため、完全な使い捨てダミーオブジェクトで同期するFが役に立たないのと同じように、個々のインスタンスで同期することは役に立ちません。

19
Thilo

言語仕様 によると:

同期メソッドは、実行前にモニター(§17.1)を取得します。

クラス(静的)メソッドの場合、メソッドのクラスのClassオブジェクトに関連付けられたモニターが使用されます。

インスタンスメソッドの場合、これに関連付けられたモニター(メソッドが呼び出されたオブジェクト)が使用されます。

これは、指定したコードでsynchronizedキーワードを指定すると、メソッドの本体を実行する前に、メソッドがthisのロックを取得することを意味します。ただし、xstaticであるため、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) {
            // ...
        }
    }
}
12
DaoWen

DoThings()メソッドを同期することにより、特定のTestSevenオブジェクトのロックを保持しています。ただし、クラスの静的変数は、オブジェクト自体の特定のインスタンスに属していません。それらはClassオブジェクトに属しますTestSeven.class。だから、あなたはどちらかのために行くことができます

synchronized (TestSeven.class){
    int current = x;
    current++;
    x = current;
}

やり過ぎているインスタンスロック内のクラスロックを取得しているdoThings()メソッド内。したがって、メソッドを静的としてマークして、Classオブジェクトのロックのみを取得することができます。

6
Arun Manivannan

xstaticであるため、doThingsメソッドの実行と同時に他のスレッドがそれを変更する可能性があります。 doThingsstaticを作成すると、これが停止します。

1
fastcodejava