web-dev-qa-db-ja.com

同期ブロックの代わりに同期メソッドを使用する利点はありますか?

例を挙げれば、同期ブロックに対する同期メソッドの利点を誰かに教えてもらえますか。

395
Warrior

例を挙げれば、synchronizedブロックに対するsynchronizedメソッドの利点を誰かに教えてもらえますか?ありがとう

ブロックに対して同期メソッドを使用することの明確な利点はありません。

おそらく唯一のもの(私はそれを利点とは言いません)はあなたがオブジェクト参照thisを含める必要がないということです。

方法:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

ブロック:

public void method() { 
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

見る?まったく利点はありません。

ブロックdoは、他のオブジェクトをロックとして使用できるのに対し、メソッドを同期するとオブジェクト全体がロックされるため、メソッドよりも優れています。

比較しなさい:

// locks the whole object
... 
private synchronized void someInputRelatedWork() {
    ... 
}
private synchronized void someOutputRelatedWork() {
    ... 
}

vs.

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) { 
        ... 
    } 
}
private void someOutputRelatedWork() {
    synchronized(outputLock) { 
        ... 
    }
}

また、メソッドが大きくなっても同期セクションを分離しておくことができます。

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) { 
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}
423
OscarRyz

唯一の本当の違いは、synchronizedブロックがどのオブジェクトに同期するかを選択できることです。同期メソッドは'this'(または同期クラスメソッドの対応するClassインスタンス)のみを使用できます。たとえば、これらは意味的に等価です。

synchronized void foo() {
  ...
}

void foo() {
    synchronized (this) {
      ...
    }
}

後者は、anyオブジェクト(多くの場合メンバー変数)の関連ロックを競合することができるため、より柔軟です。ブロックの前後に並行してコードを実行することもできますが、それでもメソッド内で実行できます。もちろん、並行コードを別々の非同期メソッドにリファクタリングすることで、同期メソッドを簡単に使用することもできます。どちらかを使用すると、コードがわかりやすくなります。

138
jcrossley3

同期メソッド

長所:

  • あなたのIDEは同期メソッドを示すことができます。
  • 構文はもっとコンパクトです。
  • 同期ブロックを別々のメソッドに分割するよう強制します。

短所:

  • これに同期するので、部外者もこれに同期することができます。
  • 同期ブロックの外側にコードを移動するのは困難です。

同期ブロック

長所:

  • ロックにプライベート変数を使用することを許可し、ロックをクラス内に強制的に留めます。
  • 同期ブロックは、変数への参照を検索することによって見つけることができます。

短所:

  • 構文はもっと複雑なので、コードを読みにくくします。

個人的には、同期が必要なものだけに焦点を絞ったクラスで同期メソッドを使用することを好みます。そのようなクラスはできるだけ小さくあるべきであり、したがって同期を見直すのは簡単なはずです。他の人は同期を気にする必要はありません。

76
iny

主な違いは、synchronizedブロックを使う場合、this以外のオブジェクトをロックすることができることです。

メッセージキューと複数のメッセージプロデューサおよびコンシューマがあるとします。プロデューサーが互いに干渉したくないが、コンシューマーはプロデューサーを待たなくてもメッセージを取得できるはずです。だから我々はただオブジェクトを作成します

Object writeLock = new Object();

そしてこれからは、プロデューサーが新しいメッセージを追加したいと思うたびに、それをロックします。

synchronized(writeLock){
  // do something
}

だから消費者はまだ読むかもしれず、生産者はロックされるでしょう。

36
cdecker

同期方式

同期メソッドには2つの効果があります。
最初に、あるスレッドがあるオブジェクトに対して同期メソッドを実行しているとき、同じオブジェクトに対して同期メソッドを呼び出す(実行を中断する)他のすべてのスレッドは、最初のスレッドがそのオブジェクトに対して終了します。

2つ目は、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの間に、事前に発生前の関係が自動的に確立されることです。これにより、オブジェクトの状態への変更がすべてのスレッドから見えるようになります。

コンストラクタは同期できないことに注意してください。コンストラクタでsynchronizedキーワードを使用すると構文エラーになります。コンストラクタの同期化は意味がありません。オブジェクトを作成している間は、オブジェクトを作成するスレッドだけがそれにアクセスできるはずです。

同期ステートメント

同期メソッドとは異なり、同期ステートメントは組み込みロックを提供するオブジェクトを指定する必要があります。ほとんどの場合、これを使用してリストまたはマップへのアクセスを同期しますが、オブジェクトのすべてのメソッドへのアクセスをブロックしたくありません。

Q:組み込みロックと同期同期は、組み込みロックまたはモニターロックと呼ばれる内部エンティティを中心に構築されています。 (API仕様では、このエンティティを単に「モニタ」と呼ぶことがよくあります。)組み込みロックは、同期の両方の側面(オブジェクトの状態への排他的アクセスの強制).

すべてのオブジェクトはそれに関連付けられた固有のロックを持ちます。慣例により、オブジェクトのフィールドへの排他的で一貫したアクセスを必要とするスレッドは、それらにアクセスする前にオブジェクトの組み込みロックを取得し、それからそれらが終了したら組み込みロックを解放する必要があります。スレッドは、ロックを獲得してからロックを解放するまでの間に、固有のロックを所有していると言われます。スレッドが組み込みロックを所有している限り、他のスレッドは同じロックを獲得できません。もう一方のスレッドは、ロックを獲得しようとするとブロックします。

package test;

public class SynchTest implements Runnable {  
    private int c = 0;

    public static void main(String[] args) {
        new SynchTest().test();
    }

    public void test() {
        // Create the object with the run() method
        Runnable runnable = new SynchTest();
        Runnable runnable2 = new SynchTest();
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(runnable,"thread-1");
        Thread thread2 = new Thread(runnable,"thread-2");
//      Here the key point is passing same object, if you pass runnable2 for thread2,
//      then its not applicable for synchronization test and that wont give expected
//      output Synchronization method means "it is not possible for two invocations
//      of synchronized methods on the same object to interleave"

        // Start the thread
        thread.start();
        thread2.start();
    }

    public synchronized  void increment() {
        System.out.println("Begin thread " + Thread.currentThread().getName());
        System.out.println(this.hashCode() + "Value of C = " + c);
//      If we uncomment this for synchronized block, then the result would be different
//      synchronized(this) {
            for (int i = 0; i < 9999999; i++) {
                c += i;
            }
//      }
        System.out.println("End thread " + Thread.currentThread().getName());
    }

//    public synchronized void decrement() {
//        System.out.println("Decrement " + Thread.currentThread().getName());
//    }

    public int value() {
        return c;
    }

    @Override
    public void run() {
        this.increment();
    }
}

同期化された方法、ブロック、同期化なしで異なる出力をクロスチェックします。

30
sudheer

注:静的同期メソッドとブロックはClassオブジェクトに対して機能します。

public class MyClass {
   // locks MyClass.class
   public static synchronized void foo() {
// do something
   }

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}
29
Peter Lawrey

Javaコンパイラがソースコードをバイトコードに変換する場合、同期メソッドと同期ブロックの処理方法が大きく異なります。

JVMが同期メソッドを実行すると、実行中のスレッドはメソッドのmethod_info構造にACC_SYNCHRONIZEDフラグが設定されていることを識別し、オブジェクトのロックを自動的に取得してメソッドを呼び出し、ロックを解除します。例外が発生すると、スレッドは自動的にロックを解除します。

一方、メソッドブロックを同期すると、オブジェクトのロックおよび例外処理を取得するためのJVMの組み込みサポートがバイパスされ、機能をバイトコードで明示的に記述する必要があります。同期ブロックを持つメソッドのバイトコードを読み取ると、この機能を管理するための追加の操作が12個以上表示されます。

これは、同期メソッドと同期ブロックの両方を生成する呼び出しを示しています。

public class SynchronizationExample {
    private int i;

    public synchronized int synchronizedMethodGet() {
        return i;
    }

    public int synchronizedBlockGet() {
        synchronized( this ) {
            return i;
        }
    }
}

synchronizedMethodGet()メソッドは、次のバイトコードを生成します。

0:  aload_0
1:  getfield
2:  nop
3:  iconst_m1
4:  ireturn

そして、これはsynchronizedBlockGet()メソッドからのバイトコードです:

0:  aload_0
1:  dup
2:  astore_1
3:  monitorenter
4:  aload_0
5:  getfield
6:  nop
7:  iconst_m1
8:  aload_1
9:  monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow

同期化されたメソッドとブロックの大きな違いの1つは、同期化されたブロックが一般にロックの範囲を縮小することです。ロックの範囲はパフォーマンスに反比例するため、コードの重要なセクションのみをロックする方が常に優れています。同期ブロックを使用する最良の例の1つは、 シングルトンパターンでのダブルチェックロック です。ここで、getInstance()メソッド全体をロックする代わりに、シングルトンインスタンスの作成に使用されるコードのクリティカルセクションのみをロックします。ロックが必要なのは1〜2回だけなので、これによりパフォーマンスが大幅に向上します。

同期メソッドを使用している間、静的同期メソッドと非静的同期メソッドの両方を混在させる場合は、特に注意する必要があります。

17
Mohammad Adil

ほとんどの場合、これを使用してリストまたはマップへのアクセスを同期しますが、オブジェクトのすべてのメソッドへのアクセスをブロックしたくはありません。

次のコードでは、リストを変更している1つのスレッドが、マップを変更しているスレッドを待ってブロックすることはありません。メソッドがオブジェクト上で同期されている場合、各メソッドは、行っている変更が競合していなくても待機する必要があります。

private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();

public void put( String s, Bar b ) {
  synchronized( myMap ) {
    myMap.put( s,b );
    // then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public void hasKey( String s, ) {
  synchronized( myMap ) {
    myMap.hasKey( s );
  }
}

public void add( Foo f ) {
  synchronized( myList ) {
    myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public Thing getMedianFoo() {
  Foo med = null;
  synchronized( myList ) {
    Collections.sort(myList);
    med = myList.get(myList.size()/2); 
  }
  return med;
}
12
Clint

シンクロナイズドブロックでは、複数のシンクロナイザを持つことができるので、複数の同時だが衝突しないことが同時に進行することができます。

7
Paul Tomblin

同期メソッドはリフレクションAPIを使用して確認できます。これは、モデル内のすべてのメソッドが同期されているなど、いくつかの規約をテストするのに役立ちます。

次のスニペットは、Hashtableのすべての同期メソッドを表示します。

for (Method m : Hashtable.class.getMethods()) {
        if (Modifier.isSynchronized(m.getModifiers())) {
            System.out.println(m);
        }
}
6
Kojotak

メソッドレベルでロックを使用するのは失礼です。メソッド全体をロックすることで、共有リソースにアクセスしないコードをロックするのはなぜですか。各オブジェクトはロックされているので、ブロックレベルの同期を実装するためにダミーオブジェクトを作成できます。 ブロックレベルはメソッド全体をロックしないため、より効率的です。

ここにいくつかの例

メソッドレベル

class MethodLevel {

  //shared among threads
SharedResource x, y ;

public void synchronized method1() {
   //multiple threads can't access
}
public void synchronized method2() {
  //multiple threads can't access
}

 public void method3() {
  //not synchronized
  //multiple threads can access
 }
}

ブロックレベル

class BlockLevel {
  //shared among threads
  SharedResource x, y ;

  //dummy objects for locking
  Object xLock = new Object();
  Object yLock = new Object();

    public void method1() {
     synchronized(xLock){
    //access x here. thread safe
    }

    //do something here but don't use SharedResource x, y
    // because will not be thread-safe
     synchronized(xLock) {
       synchronized(yLock) {
      //access x,y here. thread safe
      }
     }

     //do something here but don't use SharedResource x, y
     //because will not be thread-safe
    }//end of method1
 }

[編集]

CollectionVectorのようなHashtableの場合、ArrayListまたはHashMapが同期していない場合は同期がとられているので、synchronizedキーワードを設定するか、Collections synchronizedメソッドを呼び出す必要があります。

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
5
Maxim Shoustin

Synchronizedブロックを使用する際の重要な注意事項:ロックオブジェクトとして使用するものに注意してください。

上記のuser2277816のコードスニペットは、文字列リテラルへの参照がロックオブジェクトとして使用されているという点でこの点を示しています。文字列リテラルは自動的にJavaに組み込まれているので、問題が発生し始めるはずです。リテラル "lock"に同期するすべてのコードは同じロックを共有します。これは完全に無関係なコードの断片によるデッドロックを容易に引き起こす可能性があります。

注意が必要なのはStringオブジェクトだけではありません。値によっては、オートボクシングとvalueOfメソッドが同じオブジェクトを再利用する可能性があるため、ボックス化されたプリミティブも危険です。

詳しくは、 https://www.securecoding.cert.org/confluence/display/Java/LCK01-J.+Do+not+synchronize+on+objects+that+mayを参照してください。 +再利用する

5
Søren Boisen

唯一の違いは、同期ブロックでは、同期メソッドとは異なり、きめ細かいロックが可能です

基本的にsynchronizedブロックまたはメソッドはメモリの不整合エラーを避けることによってスレッドセーフなコードを書くために使われてきました。

この質問は非常に古く、過去7年間で多くのことが変更されました。スレッドセーフのために新しいプログラミング構成が導入されました。

synchroniedブロックの代わりに高度な並行処理APIを使用することでスレッドの安全性を達成できます。この文書 page はスレッドセーフを実現するための優れたプログラミング構成体を提供します。

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

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

同時収集 を使用すると、大量のデータ収集を管理しやすくなり、同期の必要性を大幅に減らすことができます。

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

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

Synchronizedのより良い代替は ReentrantLock で、これはLock APIを使用します。

暗黙のモニターロックと同じ基本的な振る舞いとセマンティクスを持つ、再入可能な相互排他ロック。

ロックのある例

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

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

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

この関連質問も参照してください。

同期とロック

5
Ravindra babu

Synchronizedメソッドはすべてのオブジェクトをロックするために使用されますSynchronizedブロックは特定のオブジェクトをロックするために使用されます

4
kishore

一般に、これらは使用されているオブジェクトのモニターと暗黙のthisオブジェクトについて明示的であること以外はほとんど同じです。私は時々見過ごされていると思うシンクロナイズドメソッドの1つのマイナス面は、あなたが同期するために "this"参照を使用することで外部オブジェクトが同じオブジェクトをロックする可能性を残しているということです。あなたがそれに遭遇した場合、それは非常に微妙なバグになる可能性があります。内部の明示的なオブジェクトまたは他の既存のフィールドで同期すると、この問題を回避でき、同期が完全にカプセル化されます。

3
Alex Miller

同期メソッドの場合、ロックはオブジェクトに対して取得されます。しかし、synchronizedブロックを使用する場合は、ロックを取得するオブジェクトを指定するという選択肢があります。

例:

    Class Example {
    String test = "abc";
    // lock will be acquired on String  test object.
    synchronized (test) {
        // do something
    }

   lock will be acquired on Example Object
   public synchronized void testMethod() {
     // do some thing
   } 

   }
2
Srinu Yarru

私はこれが古い質問であることを知っています、しかしここでの返事の私の速い読みで、私は本当にsynchronizedメソッドがwrongかもしれないロック。
実践中のJava Concurrency(p.72)から:

public class ListHelper<E> {
  public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...

public syncrhonized boolean putIfAbsent(E x) {
 boolean absent = !list.contains(x);
if(absent) {
 list.add(x);
}
return absent;
}

上記のコードはappearanceがスレッドセーフであることを示しています。しかし、実際にはそうではありません。この場合、ロックはクラスのインスタンスで取得されます。ただし、listが、そのメソッドを使用していない別のスレッドによって変更される可能性があります。正しいアプローチは使うことでしょう

public boolean putIfAbsent(E x) {
 synchronized(list) {
  boolean absent = !list.contains(x);
  if(absent) {
    list.add(x);
  }
  return absent;
}
}

上記のコードでは、all threadsを変更しようとしているlistが、同期ブロックが完了するまでブロックをブロックします。

2
aarbor

ここで既に述べたように、同期関数が "this"のみを使用する場合、同期ブロックはロックオブジェクトとしてユーザー定義変数を使用できます。そしてもちろん、同期させるべきあなたの機能の領域で操作することができます。しかし、同期化された関数と "this"をロックオブジェクトとして使用する関数全体をカバーするブロックとの間に違いはないと誰もが言っています。それは本当ではない、違いは両方の状況で生成されるバイトコードにあります。同期ブロック使用の場合、 "this"への参照を保持するローカル変数が割り当てられるべきです。そして結果として、私たちは関数のために少し大きいサイズを持つことになります(あなたが少数の関数しか持っていない場合は関係ありません)。

あなたがここで見つけることができる違いのより詳細な説明: http://www.artima.com/insidejvm/ed2/threadsynchP.html

2
Roman

実際問題として、同期ブロックに対する同期メソッドの利点は、それらがより馬鹿げているということです。ロックする任意のオブジェクトを選択することはできないので、文字列リテラルのロックやスレッドの下から変更される可変フィールドの内容のロックなど、バカなことをするために同期メソッドの構文を誤用することはできません。

一方、同期メソッドでは、オブジェクトへの参照を取得する可能性があるスレッドによってロックが取得されるのを防ぐことはできません。

したがって、同期化ブロックをメソッドの修飾子として使用すると、攻撃者を傷つけないように保護できます。一方、同期ブロックをプライベートファイナルロックオブジェクトと組み合わせて使用​​すると、独自のコードを保護者から保護できます。

2
Nathan Hughes

Java仕様の要約から: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

シンクロナイズドステートメント(14.17)はオブジェクトへの参照を計算する。その後、そのオブジェクトに対してロックアクションを実行しようとし、ロックアクションが正常に完了するまで先に進みません。 ...

シンクロナイズドメソッド(8.4.3.5)は、呼び出されると自動的にロック動作を実行する。その本体は、ロックアクションが正常に完了するまで実行されません。 メソッドがインスタンスメソッドの場合は、呼び出されたインスタンスに関連付けられたロック(つまり、本体の実行中にthisとして認識されるオブジェクト)をロックします。方法の)。 メソッドが静的の場合、メソッドが定義されているクラスを表すClassオブジェクトに関連付けられているロックをロックします。 ...

これらの記述に基づいて、私は以前の答えの大部分が正しいと言うでしょう、そして同期されたメソッドはそうでなければ方法がどのクラスを表すClassオブジェクトを得るかを考え出さなければならない静的メソッドに特に役に立つかもしれません定義済み。」

編集:私はもともとこれらが実際のJava仕様の引用であると思いました。このページは仕様の要約/説明にすぎません。

1
Josiah Yoder

私はこの質問がThread Safe Singleton二重チェックロックを使った遅延初期化の違いについてであると思います。特定のシングルトンを実装する必要がある場合は、この記事を常に参照しています。

さて、これはThread Safe Singletonです。

// Java program to create Thread Safe 
// Singleton class 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

 //synchronized method to control simultaneous access 
  synchronized public static GFG getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new GFG(); 
    } 
    return instance; 
  } 
} 

長所:

  1. 遅延初期化が可能です。

  2. スレッドセーフです。

短所:

  1. getInstance()メソッドは同期化されているため、複数のスレッドが同時にアクセスすることはできないため、パフォーマンスが低下します。

これは、二重チェックロックを使用した遅延初期化です。

// Java code to explain double check locking 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

  public static GFG getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (GFG.class) 
      { 
        if(instance==null) 
        { 
          // if instance is null, initialize 
          instance = new GFG(); 
        } 

      } 
    } 
    return instance; 
  } 
} 

長所:

  1. 遅延初期化が可能です。

  2. スレッドセーフです。

  3. キーワードが同期されているためパフォーマンスが低下します。

短所:

  1. 初めて、それはパフォーマンスに影響を与える可能性があります。

  2. 短所として。ダブルチェックロック方式の採用により、高性能のマルチスレッドアプリケーションに対応できます。

詳細についてはこの記事を参照してください。

https://www.geeksforgeeks.org/Java-singleton-design-pattern-practices-examples/

1
Alexandre Lage

TLDR;synchronized修飾子もsynchronized(this){...}式も使わずにsynchronized(myLock){...}を使います。ここでmyLockはプライベートオブジェクトを保持する最後のインスタンスフィールドです。


メソッド宣言でsynchronized修飾子を使用することと、メソッド本体でsynchronized(..){ }式を使用することの違いは次のとおりです。

  • メソッドのシグネチャに指定されたsynchronized修飾子
    1. 生成されたJavaDocに表示されます。
    2. Modifier.SYNCHRONIZED に対してメソッドの修飾子をテストするときに、 リフレクション によってプログラム的に決定可能です。
    3. synchronized(this) { .... }に比べて入力やインデントが少なくて済みます。
    4. (IDEによって異なります)クラスの概要とコード補完に表示されます。
    5. 非静的メソッドで宣言されている場合はthisオブジェクトをロックとして使用し、静的メソッドで宣言されている場合は親クラスを使用します。
  • synchronized(...){...}式はあなたを可能にします
    1. メソッドの本体部分の実行のみを同期する
    2. コンストラクタまたは( static )初期化ブロック内で使用されます。
    3. 同期アクセスを制御するロックオブジェクトを選択します。

ただし、ロックオブジェクトとして(synchronized(...) {...}のように)synchronized修飾子またはthisを指定したsynchronized(this) {...}を使用する場合も、同じ欠点があります。どちらも同期するためのロックオブジェクトとして独自のインスタンスを使用します。オブジェクト自体だけでなく、そのオブジェクトへの参照を保持するany他の外部オブジェクト/コードも、潜在的に深刻な副作用を伴う同期ロックとして使用する可能性があるため、これは危険です。 (パフォーマンスの低下と デッドロック )。

したがって、ベストプラクティスは、ロックオブジェクトとしてsynchronizedと組み合わせてthis修飾子もsynchronized(...)式も使用しないで、このオブジェクト専用のロックオブジェクトを使用することです。例えば:

public class MyService {
    private final lock = new Object();

    public void doThis() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }

    public void doThat() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }
}

複数のロックオブジェクトを使用することもできますが、ネストして使用した場合にこれがデッドロックにならないように、特別な注意が必要です。

public class MyService {
    private final lock1 = new Object();
    private final lock2 = new Object();

    public void doThis() {
       synchronized(lock1) {
          synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThat() and doMore().
          }
    }

    public void doThat() {
       synchronized(lock1) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doMore() may execute concurrently
        }
    }

    public void doMore() {
       synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doThat() may execute concurrently
        }
    }
}
1