10

複数のスレッドが getter メソッドを介してクラス フィールドにアクセスする場合、スレッド セーフをどのように維持しますか? 同期されたキーワードで十分ですか?

これは安全ですか:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

    private void setVal(int val) {
        this.val = val;
    }
}

または、セッターはさらに複雑になりますか?

4

6 に答える 6

17

ここでもセッターで 'synchronized' を使用すると、このコードはスレッドセーフになります。ただし、粒度が十分でない場合があります。20 個のゲッターとセッターがあり、それらがすべて同期されている場合、同期のボトルネックが発生している可能性があります。

この特定のインスタンスでは、単一の int 変数を使用して、「synchronized」を削除し、int フィールドを「volatile」とマークすると、可視性も確保されます (各スレッドは、getter を呼び出すときに「val」の最新の値を参照します)。必要に応じて十分に同期されます。たとえば、

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

val が既に 1 である場合にのみ val を 2 に設定するのは正しくありません。これには、外部ロック、またはアトミックな比較および設定メソッドが必要です。

Brian Goetz 氏らによるJava Concurrency In Practiceを読むことを強くお勧めします。Java の並行性構造を最も詳しく説明しています。

于 2008-09-23T00:22:28.010 に答える
3

Cowanのコメントに加えて、比較と保存のために次のことを行うことができます。

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

これが機能するのは、同期メソッドを介して定義されたロックが、オブジェクトのロックと暗黙的に同じであるためです(Java言語の仕様を参照)。

于 2008-09-23T00:39:05.087 に答える
2

私の理解では、getter メソッドと setter メソッドの両方で synchronized を使用する必要があり、それで十分です。

編集:これは、同期に関する詳細情報へのリンクです。

于 2008-09-23T00:19:12.013 に答える
1

クラスに含まれる変数が1つだけの場合、スレッドセーフを実現する別の方法は、既存のAtomicIntegerオブジェクトを使用することです。

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

    public int getValue(){
         return value.get();
    }

}

ただし、依存するように変数を追加すると(1つの変数の状態は別の変数の状態に依存します)、AtomicIntegerは機能しません。

「JavaConcurrencyinPractice」を読むという提案をエコーし​​ます。

于 2009-08-07T15:56:49.327 に答える
0

単純なオブジェクトの場合、これで十分な場合があります。ほとんどの場合、同期のデッドロックが発生する可能性があるため、synchronizedキーワードは避ける必要があります。

例:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

1つのスレッドのみがローカルインスタンスメンバーに対して読み取りまたは書き込みを行うことを保証します。

「Java(tm)での並行プログラミング:設計原理とパターン(Java(Addison-Wesley))」という本を読んでください。おそらくhttp://java.sun.com/docs/books/tutorial/essential/concurrency/index.htmlも役に立ちます...

于 2008-09-23T10:37:47.253 に答える
-3

同期は、スレッドの干渉とメモリの一貫性エラーから保護するために存在します。getVal() で同期することにより、コードは SomeClass の他の同期メソッドも同時に実行されないことを保証します。他の同期方法がないため、あまり価値がありません。また、プリミティブの読み取りと書き込みにはアトミック アクセスがあることに注意してください。つまり、注意深くプログラミングすれば、フィールドへのアクセスを同期する必要はありません。

同期を読み取ります。

これが-3に落とされた理由はよくわかりません。Sun の Synchronization チュートリアル (および私自身の経験) の内容を要約しているだけです。

単純なアトミック変数アクセスを使用する方が、同期コードを介してこれらの変数にアクセスするよりも効率的ですが、メモリの一貫性エラーを回避するために、プログラマはより注意を払う必要があります。追加の作業が価値があるかどうかは、アプリケーションのサイズと複雑さによって異なります。

于 2008-09-23T00:33:20.577 に答える