質問

Java では、コード内のクリティカル セクションを宣言する慣用的な方法は次のとおりです。

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

ほぼすべてのブロックが同期します this, しかし、これには特別な理由があるのでしょうか?他の可能性はありますか?どのオブジェクトを同期するかについてのベスト プラクティスはありますか?(プライベートインスタンスなど) Object?)

役に立ちましたか?

解決

まず、次のコードスニペットは同一であることに注意してください。

public void foo() {
    synchronized (this) {
        // do something thread-safe
    }
}

and:

public synchronized void foo() {
    // do something thread-safe
}

まったく同じことを行う。コードの読みやすさとスタイルを除いて、どちらにも優先順位はありません。

メソッドまたはコードブロックを同期する場合、そのようなことをなぜしているか、およびどのオブジェクトをロックしているか、< strong>目的。

また、要求しているモニター(同期オブジェクト)が必ずしも thisであるとは限らないコードのブロックをクライアント側で同期したい場合があることに注意してください、この例のように:

Vector v = getSomeGlobalVector();
synchronized (v) {
    // some thread-safe operation on the vector
}

同時プログラミングについてより多くの知識を得ることをお勧めします。舞台裏で何が起こっているのかを正確に把握すれば、それは大いに役立つでしょう。このテーマに関するすばらしい本である Javaでの同時プログラミングをご覧ください。サブジェクトをすばやく確認したい場合は、 Java Concurrency @ Sunをご覧ください。

他のヒント

以前の回答者が指摘したように、限定されたスコープのオブジェクトで同期することをお勧めします(言い換えれば、最も制限の厳しいスコープを選択して使用します)。特に、で同期するクラスのユーザーにロックの取得を許可するつもりでない限り、これは悪い考えです。

java.lang.String で同期することを選択した場合、特にいケースが発生します。文字列はインターンすることができます(実際にはほとんど常にインターン)。つまり、同じコンテンツの各文字列( JVM全体)は、裏で同じ文字列であることがわかります。つまり、文字列を同期すると、同じコンテンツの文字列もロックする別の(完全に異なる)コードセクションが実際にコードもロックします。

私はかつて本番システムのデッドロックをトラブルシューティングし、(非常に痛いほど)内容が両方とも&quot; LOCK&quot; 。

this での同期を避けるようにします。これにより、そのオブジェクトへの参照を持っている外部からのすべてのユーザーが同期をブロックできるようになります。代わりに、ローカル同期オブジェクトを作成します:

public class Foo {
    private final Object syncObject = new Object();
    …
}

今では、そのオブジェクトを使用して、だれも恐れることなく同期できます&#8220;盗用&#8221;ロック。

java.util.concurrent.locks.ReadWriteLockとして見つかったJavaで利用可能なReadWriteLocksもあることを強調するだけです。

ほとんどの使用法では、ロックを「読み取り用」と「更新用」に分けています。同期キーワードを使用するだけの場合、同じメソッド/コードブロックへのすべての読み取りは「キューに入れられます」。一度に1つのスレッドのみがブロックにアクセスできます。

ほとんどの場合、単に読み取りを行うだけであれば、同時実行の問題を心配する必要はありません。書き込みを行っているときに、同時更新(データの損失)を心配したり、書き込み中の読み取り(部分更新)を心配する必要があります。

したがって、マルチスレッドプログラミング中は読み取り/書き込みロックがより意味があります。

Mutexとして機能できるオブジェクトで同期する必要があります。現在のインスタンス( this 参照)が適切な場合(たとえば、シングルトンではない)、Javaのように、任意のオブジェクトがミューテックスとして機能するため、それを使用できます。

他の状況では、これらのクラスのインスタンスがすべて同じリソースへのアクセスを必要とする場合、複数のクラス間でMutexを共有することもできます。

作業している環境と構築しているシステムのタイプに大きく依存します。私が見たほとんどのJava EEアプリケーションでは、実際には同期の必要はありません...

個人的には、同期することは決して正しくない、またはほとんど正しくないという答えは正しいと思います。 this 誤解されています。APIに依存すると思います。クラスがスレッドセーフな実装であり、それを文書化する場合は、次のようにする必要があります。 this. 。同期によって、クラスのパブリック メソッドの呼び出し時にクラスの各インスタンスが全体としてスレッドセーフにならない場合は、プライベート内部オブジェクトを使用する必要があります。再利用可能なライブラリコンポーネント 頻繁 前者のカテゴリに分類されます。ユーザーが API を外部同期でラップすることを禁止する前に、慎重に検討する必要があります。

前者の場合、次のように使用します。 this 複数のメソッドをアトミ​​ックな方法で呼び出すことができます。1 つの例は PrintWriter です。複数行を出力し (コンソール/ロガーへのスタック トレースなど)、それらが一緒に表示されることを保証したい場合があります。この場合、同期オブジェクトを内部的に隠すという事実が非常に面倒です。もう 1 つの例は、同期コレクション ラッパーです。反復するには、コレクション オブジェクト自体を同期する必要があります。反復は複数のメソッド呼び出しで構成されているため、 できない 内部的に完全に保護します。

後者の場合は、プレーンなオブジェクトを使用します。

private Object mutex=new Object();

しかし、ロックが「java.lang.Object() のインスタンス」であるという多くの JVM ダンプやスタック トレースを見てきたので、他の人が示唆しているように、内部クラスを使用する方が役立つ場合が多いと言わざるを得ません。

とにかく、それは私の2ビットの価値です。

編集:もう 1 つ、同期するときは、 this 私はメソッドを同期し、メソッドを非常に細分化することを好みます。より明確で簡潔になったと思います。

Javaでの同期には、多くの場合、同じインスタンスでの操作の同期が含まれます。 this はクラスの異なるインスタンスメソッド(またはセクション)間で自動的に利用できる共有参照であるため、 this での同期は非常に慣用的です。

たとえば、プライベートフィールドを宣言して初期化することにより、特にロックのために別の参照を使用することは、たとえば Object lock = new Object()であり、決して必要も使用もしていません。オブジェクト内の2つ以上の非同期リソースで外部同期が必要な場合にのみ役立つと思いますが、常にこのような状況をより単純な形式にリファクタリングしようとします。

とにかく、暗黙的(同期メソッド)または明示的 synchronized(this)は、Javaライブラリでも多く使用されています。それは良いイディオムであり、該当する場合は、常に最初の選択肢でなければなりません。

同期する対象は、このメソッド呼び出しと競合する可能性のある他のスレッドが同期できるものに依存します。

this が1つのスレッドのみによって使用されるオブジェクトであり、スレッド間で共有される可変オブジェクトにアクセスしている場合、その候補を同期するのが良い候補です-で同期する共有オブジェクトを変更する別のスレッドは this を認識していないかもしれませんが、そのオブジェクトを認識しているため、this は意味がありません。

一方、 this を介した同期は、多数のスレッドが同時にこのオブジェクトのメソッドを呼び出す場合、たとえばシングルトンにいる場合に意味があります。

メソッドの実行中は常にロックを保持するため、同期メソッドは多くの場合最良のオプションではないことに注意してください。時間がかかるがスレッドセーフな部分とそれほど時間がかからないスレッドセーフでない部分が含まれている場合、メソッドの同期は非常に間違っています。

  

これでほぼすべてのブロックが同期しますが、これには特別な理由がありますか?他の可能性はありますか?

この宣言はメソッド全体を同期します。

private synchronized void doSomething() {

この宣言は、メソッド全体ではなくコードブロックの一部を同期しました。

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

Oracleドキュメントページ

から

これらのメソッドを同期させると、2つの効果があります:

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

  

他の可能性はありますか?同期するオブジェクトのベストプラクティスはありますか? (Objectのプライベートインスタンスなど)

同期には多くの可能性と代替手段があります。高レベルの同時実行 API (JDK 1.5リリース以降で使用可能)

Lock objects
Executors
Concurrent collections
Atomic variables
ThreadLocalRandom

詳細については、以下のSEの質問を参照してください。

同期とロック

Javaでsynchronized(this)を回避しますか

ベストプラクティスは、ロックを提供するためだけにオブジェクトを作成することです:

private final Object lock = new Object();

private void doSomething() {
  // thread-safe code
  synchronized(lock) {
    // thread-unsafe code
  }
  // thread-safe code
}

これを行うことで、呼び出しコードが意図しない synchronized(yourObject)行によってメソッドをデッドロックすることはないので安全です。

上記で詳細を説明した@jaredおよび@ yuval-adamへのクレジット。

チュートリアルで this を使用する人気は、初期のSun javadoc https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top