web-dev-qa-db-ja.com

Javaでsynchronized(this)を避けますか?

SOにJava同期に関する質問が表示されるたびに、synchronized(this)を避ける必要があることを指摘したい人がいます。代わりに、プライベート参照のロックが優先されると主張しています。

特定の理由のいくつかは次のとおりです。

私を含む他の人々は、synchronized(this)は(Javaライブラリでも)よく使われるイディオムであり、安全でよく理解されていると主張しています。バグがあり、マルチスレッドプログラムで何が起こっているのかわからないので、避けるべきではありません。つまり、該当する場合は使用します。

synchronized(this)も仕事をするときにthisのロックを避けることが望ましい、実際の例(foobarのないもの)を見ることに興味があります。

したがって:常にsynchronized(this)を避け、プライベート参照のロックに置き換えますか?


いくつかの詳細情報(回答が提供されると更新されます):

  • インスタンスの同期について話している
  • synchronized(this)の暗黙的(synchronizedメソッド)と明示的形式の両方が考慮されます
  • 主題に関するBlochまたは他の当局を引用する場合は、好ましくない部分(たとえば、有効なJava、スレッドセーフティに関する項目:、通常はインスタンス自体のロック、ただし、例外があります。)
  • synchronized(this)が提供する以外のロックで粒度が必要な場合、synchronized(this)は適用されないため、問題ではありません
359
eljenso

各ポイントについて個別に説明します。

  1. いくつかの邪悪なコードがロックを盗む可能性があります(これは非常に人気があり、「偶然」のバリエーションもあります)

    偶然がもっと心配です。つまり、このthisの使用は、クラスの公開インターフェイスの一部であり、文書化する必要があるということです。他のコードがロックを使用できることが望ましい場合があります。これは、Collections.synchronizedMapのようなものに当てはまります(javadocを参照)。

  2. 同じクラス内のすべての同期メソッドはまったく同じロックを使用するため、スループットが低下します

    これは過度に単純化した考え方です。 synchronized(this)を取り除くだけでは問題は解決しません。スループットを適切に同期するには、さらに注意が必要です。

  3. あなたは(不必要に)あまりにも多くの情報を公開しています

    これは#1のバリアントです。 synchronized(this)の使用はインターフェースの一部です。これを公開したくない場合は、実行しないでください。

122
Darron

まあ、最初にそれが指摘されるべきである:

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

意味的には次と同等です:

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

synchronized(this)を使用しない理由の1つです。 synchronized(this)ブロックの周りで何かをできると主張するかもしれません。通常の理由は、すべての種類の同時実行性の問題、具体的には double checked-locking problem につながる、同期チェックを行う必要がないようにすることです。比較的単純なチェックをスレッドセーフにすることです。

プライベートロックは防御メカニズムであり、決して悪い考えではありません。

また、おっしゃるように、プライベートロックは粒度を制御できます。オブジェクトに対する一連の操作は、他の操作とはまったく無関係かもしれませんが、synchronized(this)はそれらすべてへのアクセスを相互に除外します。

synchronized(this)は実際には何も提供しません。

84
cletus

Synchronized(this)を使用している間は、クラスインスタンスをロック自体として使用しています。これは、スレッド1がロックを取得している間、スレッド2が待機することを意味します

次のコードを想定してください

public void method1() {
    do something ...
    synchronized(this) {
        a ++;      
    }
    ................
}


public void method2() {
    do something ...
    synchronized(this) {
        b ++;      
    }
    ................
}

変数aを変更する方法1および変数bを変更する方法2では、2つのスレッドによる同じ変数の同時変更は避けてください。ただし、thread1変更中aおよびthread2変更中b競合状態なしで実行できます。

残念ながら、上記のコードではロックに同じ参照を使用しているため、これは許可されません。これは、スレッドが競合状態にない場合でも待機する必要があることを意味し、明らかにコードはプログラムの並行性を犠牲にします。

解決策は、2異なる変数に対して2異なるロックを使用することです。

  class Test {
        private Object lockA = new Object();
        private Object lockB = new Object();

public void method1() {
    do something ...
    synchronized(lockA) {
        a ++;      
    }
    ................
}


public void method2() {
    do something ...
    synchronized(lockB) {
        b ++;      
    }
    ................
 }

上記の例では、よりきめの細かいロックを使用しています(2つのロックの代わりに1つ(lockAおよびlockB変数aおよびbそれぞれ)、結果としてより良い同時実行が可能になりますが、一方で、最初の例よりも複雑になりました...

51
Andreas Bakurov

独断的なルールを盲目的に遵守しないことに同意しますが、「ロックを盗む」シナリオはあなたにとって非常に奇妙に思えますか?スレッドは実際にオブジェクトのロックを「外部的に」取得する可能性があり(synchronized(theObject) {...})、同期されたインスタンスメソッドで待機している他のスレッドをブロックします。

悪意のあるコードを信じていない場合は、このコードが第三者から来ている可能性があることを考慮してください(たとえば、何らかのアプリケーションサーバーを開発する場合)。

「偶然の」バージョンはあまりありそうにないが、彼らが言うように、「ばかを防げるものを作れば、誰かがより良いばかを発明するだろう」。

だから、私はそれがクラスに依存するという考え方に同意します。


eljensoの最初の3つのコメントを編集:

ロックを盗む問題を経験したことはありませんが、ここに想像上のシナリオがあります:

システムがサーブレットコンテナであり、検討しているオブジェクトがServletContext実装であるとします。コンテキスト属性は共有データであるため、そのgetAttributeメソッドはスレッドセーフでなければなりません。したがって、synchronizedとして宣言します。また、コンテナの実装に基づいてパブリックホスティングサービスを提供すると想像してみましょう。

私はあなたの顧客であり、私の「良い」サーブレットをあなたのサイトにデプロイします。コードにgetAttributeへの呼び出しが含まれていることがあります。

別の顧客を装ったハッカーが、悪意のあるサーブレットをサイトに展開します。 initメソッドに次のコードが含まれています。

 synchronized(this.getServletConfig()。getServletContext()){
 while(true){} 
} 

同じサーブレットコンテキスト(2つのサーブレットが同じ仮想ホスト上にある限り仕様で許可されている)を共有すると仮定すると、getAttributeの呼び出しは永久にロックされます。ハッカーは私のサーブレットでDoSを達成しました。

getAttributeがプライベートロックで同期されている場合、サードパーティのコードはこのロックを取得できないため、この攻撃は不可能です。

この例は不自然であり、サーブレットコンテナがどのように機能するかについて過度に単純化した見方をしていることを認めますが、私見はそれがポイントであることを証明しています。

したがって、セキュリティの考慮事項に基づいて設計を選択します。インスタンスにアクセスするコードを完全に制御できますか?スレッドがインスタンスのロックを無期限に保持する結果はどうなりますか?

14
Olivier

C#とこれに関するJavaキャンプには異なるコンセンサスがあるようです。私が見たJavaコードの大半は以下を使用しています:

// apply mutex to this instance
synchronized(this) {
    // do work here
}

一方、C#コードの大部分は間違いなくより安全なものを選択しています。

// instance level lock object
private readonly object _syncObj = new object();

...

// apply mutex to private instance level field (a System.Object usually)
lock(_syncObj)
{
    // do work here
}

C#のイディオムは確かに安全です。前述のように、インスタンスの外部から悪意のある/偶発的なロックへのアクセスを行うことはできません。 Javaコードにもこのリスクがありますただし、Javaコミュニティはやがて安全性はやや劣りますが、やや簡潔なバージョンに引き寄せられているようです

これはJavaを掘るという意味ではなく、両方の言語での私の経験を反映したものです。

11
serg10

それは状況次第です。
共有エンティティが1つのみまたは複数ある場合。

完全な動作例を参照してくださいhere

小さな紹介。

スレッドと共有可能なエンティティ
複数のスレッドが同じエンティティにアクセスすることは可能です。たとえば、単一のmessageQueueを共有する複数のconnectionThreadsです。スレッドは並行して実行されるため、あるデータを別のデータで上書きする可能性があり、混乱した状況になる可能性があります。
したがって、共有可能なエンティティに一度に1つのスレッドのみがアクセスするようにする方法が必要です。 (CONCURRENCY)。

同期ブロック
synchronized()ブロックは、共有可能なエンティティの同時アクセスを保証する方法です。
最初に、小さなアナロジー
洗面所内に洗面台(共有可能エンティティ)の2人のP1、P2(スレッド)があり、ドア(ロック)があるとします。
これで、一度に1人が洗面台を使用するようになります。
アプローチは、ドアがロックされているときにP1でドアをロックすることです。P2は、p1が作業を完了するまで待機します
P1はドアのロックを解除します
その後、p1のみが洗面台を使用できます。

構文。

synchronized(this)
{
  SHARED_ENTITY.....
}

「これ」は、クラスに関連付けられた固有のロックを提供しました(Java開発者は、各オブジェクトがモニターとして機能できるようにObjectクラスを設計しました)。上記のアプローチは、共有エンティティが1つだけで複数のスレッド(1:N)がある場合に正常に機能します。
enter image description hereN共有可能エンティティ-Mスレッド
次に、洗面所内に洗面台が2つあり、ドアが1つしかない状況を考えます。前のアプローチを使用している場合、p1のみが一度に1つの洗面台を使用でき、p2は外部で待機します。誰もB2(洗面台)を使用していないため、リソースの浪費です。
賢明なアプローチは、洗面所内に小さな部屋を作り、洗面台ごとに1つのドアを提供することです。このようにして、P1はB1にアクセスでき、P2はB2にアクセスでき、その逆も同様です。

washbasin1;  
washbasin2;

Object lock1=new Object();
Object lock2=new Object();

  synchronized(lock1)
  {
    washbasin1;
  }

  synchronized(lock2)
  {
    washbasin2;
  }

enter image description here
enter image description here

スレッドの詳細を参照してください----> here

10
Rohit Singh

Java.util.concurrentパッケージは、スレッドセーフコードの複雑さを大幅に削減しました。逸話的な証拠しかありませんが、synchronized(x)で見たほとんどの作業は、ロック、セマフォ、またはラッチを再実装しているようですが、低レベルのモニターを使用しています。

これを念頭に置いて、これらのメカニズムのいずれかを使用して同期することは、ロックをリークするのではなく、内部オブジェクトで同期することに似ています。これは、2つ以上のスレッドによってモニターへのエントリを確実に制御できるという点で有益です。

7
jamesh
  1. 可能な場合はデータを不変にします(final変数)
  2. 複数のスレッド間で共有データの変更を避けられない場合は、高レベルのプログラミング構造を使用してください[例:詳細なLock API]

ロックは共有リソースへの排他的アクセスを提供します。一度に1つのスレッドのみがロックを取得でき、共有リソースへのすべてのアクセスにはロックを最初に取得する必要があります。

ReentrantLockインターフェイスを実装するLockを使用するサンプルコード

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

ロックオーバー同期の利点(this)

  1. 同期化されたメソッドまたはステートメントを使用すると、すべてのロックの取得と解放がブロック構造の方法で強制的に発生します。

  2. ロックの実装は、以下を提供することにより、同期化されたメソッドおよびステートメントの使用を超える追加機能を提供します。

    1. ロックを取得しようとするノンブロッキングの試み(tryLock()
    2. 中断できるロックを取得しようとしました(lockInterruptibly()
    3. タイムアウトする可能性のあるロックを取得しようとしました(tryLock(long, TimeUnit))。
  3. Lockクラスは、次のような暗黙のモニターロックとはまったく異なる動作とセマンティクスも提供できます。

    1. 保証された順序
    2. 再入不可の使用
    3. デッドロック検出

さまざまなタイプのLocksに関するこのSEの質問をご覧ください。

同期とロック

Synchroniedブロックの代わりに高度な同時実行APIを使用して、スレッドセーフを実現できます。このドキュメント page は、スレッドセーフを実現するための優れたプログラミング構造を提供します。

オブジェクトのロック 多くの並行アプリケーションを簡素化するロックイディオムをサポートします。

Executors スレッドを起動および管理するための高レベルAPIを定義します。 Java.util.concurrentによって提供されるエグゼキューター実装は、大規模アプリケーションに適したスレッドプール管理を提供します。

Concurrent Collections 大量のデータコレクションの管理を容易にし、同期の必要性を大幅に削減できます。

原子変数 には、同期を最小限に抑え、メモリの一貫性エラーを回避する機能があります。

ThreadLocalRandom(JDK 7では)からの擬似乱数の効率的な生成を提供します複数のスレッド。

他のプログラミング構成については、 Java.util.concurrent および Java.util.concurrent.atomic パッケージも参照してください。

6
Ravindra babu

それを決めた場合:

  • 必要なことは、現在のオブジェクトをロックすることです。そして
  • メソッド全体よりも小さい粒度でロックしたい場合。

それから、synchronizezd(this)のタブーは表示されません。

一部の人々は、どのオブジェクトが実際に同期されているかを「読みやすい」と考えているため、メソッドのコンテンツ全体で(メソッドを同期化するのではなく)synchronized(this)を意図的に使用します。人々が情報に基づいた選択をしている限り(例えば、そうすることで余分なバイトコードをメソッドに実際に挿入し、これが潜在的な最適化にノックオン効果をもたらす可能性があることを理解している)、これに関する問題は特に見られません。プログラムの並行動作を常に文書化する必要があります。そのため、「同期」が動作を公開するという議論はそれほど説得力がないとは思いません。

どのオブジェクトのロックを使用すべきかという質問については、現在のオブジェクトの同期に何の問題もないと思いますこれがあなたがしていることの論理とクラスが通常どのように使用されるかによって予想される場合。たとえば、コレクションの場合、論理的にロックすると予想されるオブジェクトは一般にコレクション自体です。

5
Neil Coffey

Brian GoetzによるJava Concurrency In Practiceと呼ばれる本には、これらのそれぞれがなぜ重要な技術であるかについての良い説明があると思います。彼は1つのポイントを非常に明確にしています。オブジェクトの状態を保護するには、同じロック「どこでも」を使用する必要があります。同期化されたメソッドとオブジェクトの同期化は、しばしば密接に関連しています。例えば。ベクターはすべてのメソッドを同期します。ベクターオブジェクトへのハンドルがあり、「存在しない場合にプット」を実行する場合、単にベクター自体の個々のメソッドを同期するだけでは、状態の破損から保護されません。 synchronized(vectorHandle)を使用して同期する必要があります。これにより、ベクトルへのハンドルを持つすべてのスレッドが同じロックを取得し、ベクトルの全体的な状態を保護します。これは、クライアント側ロックと呼ばれます。事実として、ベクターはすべてのメソッドを同期(これ)/同期するため、オブジェクトvectorHandleで同期すると、ベクトルオブジェクトの状態が適切に同期されます。スレッドセーフコレクションを使用しているからといって、スレッドセーフであると考えるのは愚かです。これがまさにConcurrentHashMapが明示的にputIfAbsentメソッドを導入した理由です-そのような操作をアトミックにするためです。

要約すれば

  1. メソッドレベルで同期すると、クライアント側のロックが可能になります。
  2. プライベートロックオブジェクトがある場合-クライアント側のロックが不可能になります。クラスに「存在しない場合に置く」タイプの機能がないことがわかっている場合、これは問題ありません。
  3. ライブラリを設計している場合-これで同期するか、メソッドを同期する方が賢明です。クラスをどのように使用するかを決める立場にあることはめったにないからです。
  4. Vectorがプライベートロックオブジェクトを使用していた場合-「存在しない場合に置く」権限を取得することは不可能でした。クライアントコードはプライベートロックへのハンドルを取得しないため、EXACT SAME LOCKを使用してその状態を保護するという基本的なルールを破ります。
  5. このメソッドまたは同期されたメソッドの同期には、他の人が指摘したように問題があります-誰かがロックを取得し、それを解放することはできません。他のすべてのスレッドは、ロックが解除されるのを待ち続けます。
  6. ですから、あなたが何をしているかを知り、正しいものを採用してください。
  7. 誰かがプライベートロックオブジェクトを持つことでより良い粒度が得られると主張しました-例えば2つの操作が関連していない場合-異なるロックによって保護され、スループットが向上します。しかし、これはデザインの匂いであり、コードの匂いではないと思います-2つの操作がまったく無関係である場合、なぜそれらは同じクラスの一部ですか?なぜクラスクラブは無関係な機能を使用する必要があるのですか?ユーティリティクラスの場合もありますか?うーん-同じインスタンスを介して文字列操作とカレンダーの日付フォーマットを提供するユーティリティ?? ...少なくとも私には意味がありません!!
4
Sandesh Sadhale

いいえ、できませんalways。ただし、特定のオブジェクトに複数の懸念があり、それ自体がスレッドセーフである必要がある場合は、それを避ける傾向があります。たとえば、「ラベル」フィールドと「親」フィールドを持つ可変データオブジェクトがあるとします。これらはスレッドセーフである必要がありますが、一方を変更しても他方の書き込み/読み取りをブロックする必要はありません。 (実際には、フィールドをvolatile宣言するか、Java.util.concurrentのAtomicFooラッパーを使用することでこれを回避します)。

同期は、スレッドがお互いにどのように動作することを許可されるかを正確に考えるのではなく、大きなロックダウンを平手打ちするため、一般的に少し不器用です。 synchronized(this)の使用は、「ロックを保持している間は誰もこのクラスでanythingを変更できない」と言っているため、さらに面倒で反社会的です。実際にどのくらいの頻度でそれを行う必要がありますか?

もっと細かいロックが必要です。すべてを変更しないようにしたい場合(オブジェクトをシリアル化している場合など)でも、同じことを達成するためにすべてのロックを取得することができます。 synchronized(this)を使用する場合、なぜ同期しているのか、または副作用が何であるかは明確ではありません。 synchronized(labelMonitor)、またはさらに優れたlabelLock.getWriteLock().lock()を使用すると、何をしているか、クリティカルセクションの効果が何に限定されるかが明確になります。

3
Andrzej Doyle

短い答え:違いを理解し、コードに応じて選択する必要があります。

ロングアンサー:一般的に、競合を減らすためにsynchronize(this)を避けようとしますが、プライベートロックは複雑さを増します知っておいてください。したがって、適切なジョブに適切な同期を使用します。マルチスレッドプログラミングの経験があまりない場合は、インスタンスのロックに固執し、このトピックを読み上げます。 (つまり:synchronize(this)を使用しても、クラスが自動的に完全にスレッドセーフになるわけではありません。)これは簡単なトピックではありませんが、それに慣れて、synchronize(this)を使用するかどうかの答えは自然に得られます。

3
tcurdt

ロックは、可視性または一部のデータを同時変更から保護するために使用されますレースにつながる可能性があります。

プリミティブ型の操作をアトミックにするだけの場合、AtomicIntegerなどのオプションを使用できます。

ただし、xおよびy座標のように互いに関連する2つの整数があり、これらは互いに関連しており、アトミックな方法で変更する必要があるとします。次に、同じロックを使用してそれらを保護します。

ロックは、相互に関連する状態のみを保護する必要があります。劣らず、これ以上。各メソッドでsynchronized(this)を使用すると、クラスの状態が無関係であっても、関係のない状態を更新しても、すべてのスレッドが競合に直面します。

class Point{
   private int x;
   private int y;

   public Point(int x, int y){
       this.x = x;
       this.y = y;
   }

   //mutating methods should be guarded by same lock
   public synchronized void changeCoordinates(int x, int y){
       this.x = x;
       this.y = y;
   }
}

上の例では、xyが関連しているため、2つの異なるメソッドではなく、xyの両方を変更するメソッドが1つしかありません。 xyを別々に変更すると、スレッドセーフではなくなります。

この例は、単にデモを行うためのものであり、必ずしも実装方法を示すものではありません。それを行う最善の方法は、不変にすることです。

Pointの例とは反対に、@ Andreasによって既に提供されているTwoCountersの例があります。この例では、2つの異なるロックによって保護されている状態が互いに無関係です。

無関係な状態を保護するために異なるロックを使用するプロセスは、と呼ばれます。ロックストライピングまたはロックスプリッティング

2
Narendra Pathai

すでに述べたように、同期化された関数が「this」のみを使用する場合、同期ブロックはユーザー定義変数をロックオブジェクトとして使用できます。そしてもちろん、同期する必要のある関数の領域を操作することもできます。

ただし、同期オブジェクトと、ロックオブジェクトとして「this」を使用する関数全体をカバーするブロックとの間に違いはないと誰もが言います。それは真実ではありません、両方の状況で生成されるバイトコードに違いがあります。同期ブロックを使用する場合は、「this」への参照を保持するローカル変数を割り当てる必要があります。その結果、関数のサイズが少し大きくなります(関数の数が少ない場合は関係ありません)。

ここにある違いの詳細な説明: http://www.artima.com/insidejvm/ed2/threadsynchP.html

また、次の観点から、同期ブロックの使用は適切ではありません。

Synchronizedキーワードは1つの領域で非常に制限されています。同期ブロックを終了するとき、そのロックを待機しているすべてのスレッドのブロックを解除する必要がありますが、ロックを取得できるのはそのうちの1つだけです。他のすべてのユーザーは、ロックが取得されたことを確認し、ブロックされた状態に戻ります。無駄な処理サイクルだけではありません。多くの場合、スレッドのブロックを解除するコンテキスト切り替えには、ディスクからのメモリのページングも含まれます。これは非常に、非常に高価です。

この分野の詳細については、この記事を読むことをお勧めします。 http://Java.dzone.com/articles/synchronized-considered

1
Roman

thisで同期しない理由は、複数のロックが必要になる場合があるためです(2番目のロックは、よく考えて削除されることがよくありますが、それでも中間状態で必要です)。 thisをロックする場合、2つのロックのどちらがthisであるかを常に覚えておく必要があります。プライベートオブジェクトをロックすると、変数名がそのことを示します。

読者の観点からすると、thisでロックが発生している場合は、常に2つの質問に答える必要があります。

  1. thisによってどのようなアクセスが保護されていますか?
  2. 1つのロックで本当に十分ですか、誰かがバグを導入しませんでしたか?

例:

class BadObject {
    private Something mStuff;
    synchronized setStuff(Something stuff) {
        mStuff = stuff;
    }
    synchronized getStuff(Something stuff) {
        return mStuff;
    }
    private MyListener myListener = new MyListener() {
        public void onMyEvent(...) {
            setStuff(...);
        }
    }
    synchronized void longOperation(MyListener l) {
        ...
        l.onMyEvent(...);
        ...
    }
}

2つのスレッドがBadObjectの2つの異なるインスタンスでlongOperation()を開始する場合、それらはロックを取得します。 l.onMyEvent(...)を呼び出すとき、スレッドはどちらも他のオブジェクトのロックを取得できないため、デッドロックが発生します。

この例では、短い操作用と長い操作用の2つのロックを使用して、デッドロックを解消できます。

これは本当に他の答えを補足するものですが、ロックにプライベートオブジェクトを使用することに対する主な反対が、ビジネスロジックに関連しないフィールドでクラスを混乱させる場合、Project Lombokは @Synchronized を持ちますコンパイル時にボイラープレートを生成します。

@Synchronized
public int foo() {
    return 0;
}

にコンパイルする

private final Object $lock = new Object[0];

public int foo() {
    synchronized($lock) {
        return 0;
    }
}
1
Michael

依存関係のないコードのアトミックな部分の一意のプライベート参照の可能な解決策についてのみ言及したいと思います。ロック付きの静的ハッシュマップと、スタック情報(完全なクラス名と行番号)を使用して必要な参照を自動的に作成するatomic()という名前の単純な静的メソッドを使用できます。次に、新しいロックオブジェクトを作成せずに、同期メソッドでこのメソッドを使用できます。

// Synchronization objects (locks)
private static HashMap<String, Object> locks = new HashMap<String, Object>();
// Simple method
private static Object atomic() {
    StackTraceElement [] stack = Thread.currentThread().getStackTrace(); // get execution point 
    StackTraceElement exepoint = stack[2];
    // creates unique key from class name and line number using execution point
    String key = String.format("%s#%d", exepoint.getClassName(), exepoint.getLineNumber()); 
    Object lock = locks.get(key); // use old or create new lock
    if (lock == null) {
        lock = new Object();
        locks.put(key, lock);
    }
    return lock; // return reference to lock
}
// Synchronized code
void dosomething1() {
    // start commands
    synchronized (atomic()) {
        // atomic commands 1
        ...
    }
    // other command
}
// Synchronized code
void dosomething2() {
    // start commands
    synchronized (atomic()) {
        // atomic commands 2
        ...
    }
    // other command
}
0

synchronized(this)をロックメカニズムとして使用しないでください。これにより、クラスインスタンス全体がロックされ、デッドロックが発生する可能性があります。このような場合、クラス全体がロックされないように、特定のメソッドまたは変数のみをロックするようにコードをリファクタリングします。 Synchronisedはメソッドレベル内で使用できます。
synchronized(this)を使用する代わりに、以下のコードはメソッドをロックする方法を示しています。

   public void foo() {
if(operation = null) {
    synchronized(foo) { 
if (operation == null) {
 // enter your code that this method has to handle...
          }
        }
      }
    }
0
surendrapanday

この質問はすでに解決できたかもしれませんが、2019年の私の2セント。

あなたが何をしているのか知っていれば「this」のロックは悪くありませんが、「this」のロックの裏側です(残念ながらメソッド定義の同期キーワードで許可されています)。

クラスのユーザーが実際にロックを「盗む」ことができるようにする(つまり、他のスレッドがロックを処理できないようにする)場合、実際には、すべての同期メソッドを別の同期メソッドの実行中などに待機させる必要があります。それは意図的であり、よく考えられている必要があります(したがって、ユーザーが理解できるように文書化されている必要があります)。

さらに詳しく説明すると、逆に、アクセスできないロックをロックした場合(ロックを「盗む」ことができず、完全に制御できるなど)、「獲得」(または「失う」こと)を知る必要があります。 ..)。

私にとっての問題は、メソッド定義シグニチャーの同期キーワードにより、プログラマーが簡単になりすぎることです考えないで何をロックするかについて、これは、あなたが望んでいない場合に考えるべき非常に重要なことですマルチスレッドプログラムで問題が発生する。

「通常」クラスのユーザーにこれらのことをさせたくない、または「通常」あなたがしたいことを主張することはできません...それはあなたがコーディングしている機能に依存します。すべてのユースケースを予測することはできないため、経験則を作成することはできません。

例えば内部ロックを使用するが、出力をインターリーブさせたくない場合、複数のスレッドから使​​用するのに苦労するプリントライター。

クラスの外部からロックにアクセスできるかどうかは、クラスの機能に基づいてプログラマーが決定することです。これはAPIの一部です。たとえば、それを使用するコードの変更を壊す危険を冒さずに、synchronized(this)からsynchronized(provateObjet)に移動することはできません。

注1:明示的なロックオブジェクトを使用してそれを公開することで、同期(これ)が達成するものを達成できることは知っていますが、あなたの行動が十分に文書化されており、実際に「これ」のロックが意味することを知っている場合は不要だと思います.

注2:何らかのコードが誤ってロックを盗んでいる場合はバグであり、解決する必要があるという議論には同意しません。これはある意味では、すべてのメソッドがパブリックであるように意図されていなくても、すべてのメソッドをパブリックにできると言っているのと同じ議論です。誰かが「偶然」プライベートメソッドを意図していると呼んでいる場合、それはバグです。なぜ最初にこの事故を可能にするのでしょうか!!!あなたのロックを盗む能力があなたのクラスにとって問題であるならば、それを許可しないでください。それと同じくらい簡単。

0
Amit Mittal

Synchronized(this)を使用する良い例です。

// add listener
public final synchronized void addListener(IListener l) {listeners.add(l);}
// remove listener
public final synchronized void removeListener(IListener l) {listeners.remove(l);}
// routine that raise events
public void run() {
   // some code here...
   Set ls;
   synchronized(this) {
      ls = listeners.clone();
   }
   for (IListener l : ls) { l.processEvent(event); }
   // some code here...
}

ここで見ることができるように、ここで同期を使用して、長時間(おそらくrunメソッドの無限ループ)といくつかの同期メソッドを簡単に連携させます。

もちろん、プライベートフィールドで同期を使用して非常に簡単に書き換えることができます。ただし、同期メソッドを使用した設計が既にある場合(つまり、派生クラスであるレガシークラスの場合、synchronized(this)が唯一のソリューションになります)。

0
Bart Prokop

それはあなたがしたいタスクに依存しますが、私はそれを使いません。また、最初にsynchronize(this)を実行して、達成したいスレッドの節約を実行できなかったかどうかを確認しますか?いくつかの素敵なものもあります APIのロック あなたを助けるかもしれません:)

0
Harald Schilly