0

メソッド/フィールドが「this」で同期されているオブジェクトがあるとします。この質問は、実際には「これ」に関するものです。「これ」の参照が何を意味するのか、私は苦労していると思います。

したがって、私たちの目的は次のとおりです。

class A {
    private Field a;
    private Field b;

    public synchronized void doSomething() {
       //something with a
    }

    public synchronized void somethingElse() {
       //do something as with b
    }

}

次に、A オブジェクトを受け取り、doSomething および somethingElse メソッドを介して a および b を処理する別のオブジェクトまたはメソッドがあります。したがって、A オブジェクトを処理している間は状態の一貫性を保つ必要があるため、同期します。これらの A オブジェクトが Map の値であるとしましょう。次に、値を繰り返し処理し、自分が行うことを実行します。したがって、問題は、次の方法で実行するのがスレッドセーフかどうかです。

 for(A aObject : map.values()) {
     synchronized(aObject) {
          aObject.doSomething(); 
          aObject.somethingElse();
     }
 }

「this」の参照がaObjectと同じ参照であれば問題ないと思います。しかし、次のようにするとどうなりますか。

for(A aObject : map.values()) {
      A anotherReference = aObject;

      synchronized(anotherReference) {
         anotherReference.doSomething(); 
         anotherReference.somethingElse();
      }

}

まだスレッドセーフですか?つまり、ロック参照のローカル コピーを同期できますか?

注: これは、コードで行う必要があることを単純化しすぎています。

4

3 に答える 3

2

同期モニターは、参照ではなく参照されるオブジェクトに属しているため、2つのforループは同等であり、両方とも同じオブジェクトで同期します。

同期メソッド

public synchronized void foo() {
  // do stuff
}

とまったく同じです

public void foo() {
  synchronized(this) {
    // do stuff
  }
}

だからループで

for(A aObject : map.values()) {
    synchronized(aObject) {
         aObject.doSomething(); 
         aObject.somethingElse();
    }
}

同期されたブロックは、メソッドが使用するのdoSomething()と同じモニターをロックしています。doSomethingElse()同期されたブロックから得られることは、他のスレッドがこれら2つの呼び出しの間にある同じAインスタンスでこれらのメソッドのいずれかを忍び込んで呼び出すことができないことです。

于 2012-11-17T13:25:14.087 に答える
1

あなたは参照が何であるかについて混乱しているように見えるので、私はそれらを読みに行きます. 同期ブロックを使用すると、参照自体ではなく、参照が参照するオブジェクト インスタンスで同期されます。

例えば:

Object a = new Object();
Object b = a;

synchronized(a) { ... }
synchronized(b) { ... }

これら 2 つの同期ブロックは、同じ Object インスタンスab参照しているため、同じ Object インスタンスで同期しています。

それに続いて、同期されたメソッドは、参照で同期することと同じthisです。

例えば:

public class A { public synchronized void doStomething() { ... } public void doSomethingElse() { synchronized(this) { ... } } }

これらのメソッドはどちらも、として知られる自己参照を使用して、同じ Object インスタンス (現在のインスタンス) で同期していthisます。どちらの例も他の例として書き直すことができ、それらは同等です。

したがって、元の例に戻って、(最初の例のように)参照を介してオブジェクト インスタンスを外部で同期すると、それ自体で内部的に同期するオブジェクトと同じことを行っていることを理解していただければ幸いです。

ラップすると、最後の例は、呼び出し元がコレクションに関して2つの操作がアトミックに実行されるようにするため、同期コレクションで作業するときの一般的なイディオムです。

例えば:

// this will result in a List where all methods are internally synchronized
List<Object> syncList = Collections.synchronizedList(new ArrayList<Object>());

// i can safely perform an atomic operation on the List using this pattern
synchronized(syncList) {
  if(syncList.isEmpty()) { // <- synchronized method call
    syncList.add(...); // <- synchronized method call
  }
}
于 2012-11-17T13:32:47.583 に答える
0

Javaには、同期メソッド同期ステートメントの2つの基本的な同期イディオムがあります。

次のコードのように、最初のイディオム(同期メソッド)を使用する場合:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

主な効果は2つあります。

1)同じオブジェクトで同期されたメソッドの2つの呼び出しがインターリーブすることはできません。1つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまで実行を一時停止します。

2)同期されたメソッドが終了すると、同じオブジェクトに対する同期されたメソッドの後続の呼び出しとの発生前の関係が自動的に確立されます。これにより、オブジェクトの状態への変更がすべてのスレッドに表示されることが保証されます。

同期されたコードを作成する別の方法は、同期されたステートメントを使用することです。同期メソッドとは異なり、同期ステートメントは、組み込みロックを提供するオブジェクトを指定する必要があります。

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

コードでは、これらのイディオムの両方を使用しています。次に、クラスメソッドはすでに同期されたメソッドであるため、最初のforループはsynchronized(aObject)を必要としません。

ソース:http ://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

しかし、クラスメソッドが同期されていなかったとしましょう。2番目のコード例:

for(A aObject : map.values()) {
      A anotherReference = aObject;

      synchronized(anotherReference) {
         anotherReference.doSomething(); 
         anotherReference.somethingElse();
      }

}

Javaでは、すべてのオブジェクトに固有のロックが関連付けられているため、引き続き機能します。同期(オブジェクトo)を呼び出すと、オブジェクトに関連付けられたロックを取得します:anotherReference、この場合はaObjectです。

T1とT2の2つのスレッドについて考えてみましょう。T1がT2の前にこれをforループと呼ぶと、aObjectに関連付けられた組み込みロックを取得し、T1がdoSomenthing()とsomethinElse()の両方のメソッドを終了するまで、T2は同じことを実行できません。

于 2012-11-17T13:07:02.520 に答える