Synchronized Blockの代わりにSynchronized Methodを使用する利点はありますか?


401

例を使用して、同期ブロックに対する同期メソッドの利点を教えてもらえますか?




1
@cletusこの質問は、stackoverflow.com
福沢幸夫

回答:


431

例を使用して、同期ブロックに対する同期メソッドの利点を誰かに教えてもらえますか?ありがとう。

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

おそらく唯一のもの(しかし、私はそれを利点とは言わないでしょう)は、オブジェクト参照を含める必要がないことです this

方法:

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

ブロック:

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

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

ただし、ブロックにメソッドよりも利点があります。メソッドを同期するとオブジェクト全体がロックされますが、別のオブジェクトをロックとして使用できるため、ほとんどの場合、柔軟性に優れています。

比較:

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

// 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
}

44
APIの利用者にとっての利点は、メソッド宣言でsynchronizedキーワードを使用すると、メソッドがオブジェクトインスタンスで同期し、(おそらく)スレッドセーフであることも明示的に宣言されることです。
スクラブ

59
私はこれが古い質問であることを知っていますが、「これ」で同期することは一部のサークルではアンチパターンと見なされています。意図しない結果は、クラスの外で誰かが「this」に等しいオブジェクト参照をロックし、他のスレッドがクラス内のバリアを通過できないようにして、デッドロック状態を作成する可能性があることです。「private final Object = new Object();」を作成する 純粋にロック目的のための変数は、よく使用されるソリューションです。 この問題に直接関連する別の質問があります。
justin.hughey 2013年

30
「メソッドを同期すると、クラス全体がロックされます。」これは正しくありません。完全なクラスをロックするのではなく、完全なインスタンスをロックします。同じクラスの複数のオブジェクトは、独自のロックをすべて保持しています。:)
あいさつ

4
これについて興味深い点は、同期メソッドを使用すると、生成されたバイトコードの命令が1つ少なくなることです。これは、メソッドの署名に同期ビットが組み込まれているためです。バイトコードの長さは、メソッドがインライン化されるかどうかの要素であるため、ブロックをメソッドシグネチャに移動すると、判断が異なる場合があります。理論的にはとにかく。保存される単一のバイトコード命令に基づいて設計を決定することはありません。これはひどい考えのようです。しかし、それでも違いあります。=)
corsiKa 2014年

2
@corsiKa:複数の指示を保存します。synchronizedブロックは、2つの命令を使用して実装され、monitorenter且つmonitorexitプラスそれを確実に例外ハンドラがmonitorexitあっても例外的な場合に呼び出されます。synchronizedメソッドを使用すると、すべて保存されます。
Holger

139

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

synchronized void foo() {
  ...
}

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

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


後者は、foo()のすべてのコードを同期する必要がない場合にもメリットがあります。
エヴァン、

1
これは真実ですが、「戦士」が尋ねた「同期メソッドの利点」には何もありません。
OscarRyz、2009

76

同期メソッド

長所:

  • IDEは同期されたメソッドを示すことができます。
  • 構文はよりコンパクトです。
  • 同期されたブロックを個別のメソッドに強制的に分割します。

短所:

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

同期ブロック

長所:

  • ロックにプライベート変数を使用して、ロックをクラス内にとどまらせることができます。
  • 同期ブロックは、変数への参照を検索することで見つけることができます。

短所:

  • 構文が複雑になるため、コードが読みにくくなります。

個人的には、同期が必要なものにのみ焦点を当てたクラスで同期メソッドを使用することを好みます。このようなクラスは可能な限り小さくする必要があるため、同期を簡単に確認できます。他のものは同期を気にする必要はありません。


「クラスに留まる」とは、「オブジェクトに留まる」という意味ですか、それとも何か不足していますか?
OldPeculier 14

36

主な違いは、同期ブロックを使用すると、これ以外のオブジェクトをロックする可能性がありこれにより柔軟性が大幅に向上します。

メッセージキューと、複数のメッセージプロデューサーとコンシューマーがあるとします。プロデューサーが互いに干渉することは望ましくありませんが、コンシューマーはプロデューサーを待たずにメッセージを取得できる必要があります。したがって、オブジェクトを作成するだけです

Object writeLock = new Object();

そして、これからプロデューサーが新しいメッセージを追加したいときはいつでも、それをロックします。

synchronized(writeLock){
  // do something
}

そのため、コンシューマーは引き続き読み取り、プロデューサーはロックされます。


2
あなたの例は非破壊的な読み取りに限定されています。読み取りがキューからメッセージを削除する場合、プロデューサーがキューに書き込むときにある時点で行われると失敗します。
2013

30

同期方法

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

第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();
    }
}

同期されたメソッド、ブロック、同期なしのさまざまな出力をクロスチェックします。


10
+1は、これまでのところ、コンストラクターを同期できないことを言及している唯一のものです。つまり、コンストラクターで実際に使用できるオプションはSynchronizedブロックのみです。
ef2011年

私は指示どおりにコードをテストしましたが、Cは常に0で、次に-2024260031であり、ハッシュコードを変更する唯一のものです。どのような行動が見られるべきですか?
Justin Johnson

:あなたは、コンテンツが提供された記事の下に引用されている必要がありますdocs.oracle.com/javase/tutorial/essential/concurrency/...docs.oracle.com/javase/tutorial/essential/concurrency/...
ラビンドラBabuの

29

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

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

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

18

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

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

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

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

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回だけなので、これによりパフォーマンスが大幅に向上します。

同期メソッドを使用する場合、静的同期メソッドと非静的同期メソッドの両方を混在させる場合は、さらに注意が必要です。


1
バイトコード同期メソッドを見ると、バイトコードはよりコンパクトでシンプルなので、同期ブロックの方が速くないのはなぜですか?
eatSleepCode

@eatSleepCodeこれは、JVMによってさらに「コンパイル」されるバイトコードであることに注意してください。JVMが必要に追加するmonitorentermonitorexit、コードを実行する前に。
Philip Couling

12

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

次のコードでは、リストを変更する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;
}

7

同期されたブロックを使用すると、複数のシンクロナイザーを使用できるため、複数の同時ではあるが競合しないものが同時に進行できます。


6

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

次のスニペットは、Hashtableのすべての同期されたメソッドを出力します。

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

5

同期ブロックの使用に関する重要な注意:ロックオブジェクトとして何を使用するかに注意してください!

上記のuser2277816のコードスニペットは、文字列リテラルへの参照がロックオブジェクトとして使用されるという点でこの点を示しています。文字列リテラルがJavaで自動的にインターンされ、問題が発生し始めることを理解してください。リテラル「ロック」で同期するすべてのコードは、同じロックを共有します。これにより、完全に関連のないコード部分でデッドロックが発生する可能性があります。

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

詳細については、https//www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reusedを参照して ください。


5

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

ここにいくつかの例

メソッドレベル

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
 }

[編集]

以下のためCollectionのようなVectorおよびHashtableそれらが時に同期されているArrayListHashMapではなく、あなたがキーワードまたはメソッドを同期呼び出しコレクションを同期して設定する必要が:

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

5

唯一の違い:同期されたブロックは、同期された方法とは異なり、詳細なロックを可能にします

基本的に、synchronizedブロックまたはメソッドは、メモリの不整合エラーを回避することによってスレッドセーフコードを記述するために使用されてきました。

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

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

ロックオブジェクトは、多くの同時アプリケーションを簡略化するロックイディオムをサポートしています。

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

並行コレクションを使用すると、データの大規模なコレクションを管理しやすくなり、同期の必要性を大幅に減らすことができます。

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

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

同期のより良い代替品は、API を使用する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()
     }
   }
 }

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

この関連する質問も参照してください:

同期とロック


4

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


3

一般に、これらは、使用されているオブジェクトのモニターと暗黙的なthisオブジェクトについて明示的であることを除いて、ほとんど同じです。見落とされがちな同期化メソッドの欠点の1つは、「this」参照を使用して同期化すると、外部オブジェクトが同じオブジェクトをロックする可能性が残ることです。それに遭遇した場合、それは非常に微妙なバグになる可能性があります。内部の明示的なオブジェクトまたは他の既存のフィールドで同期すると、この問題を回避して、同期を完全にカプセル化できます。


2

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

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


2

同期されたメソッドの場合、オブジェクトのロックが取得されます。ただし、同期ブロックを使用する場合は、ロックを取得するオブジェクトを指定するオプションがあります。

例:

    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

私はこれが古い質問であることを知っていますが、ここでの応答をざっと読んだだけで、synchronizedメソッドが誤ったロックである可能性があることを誰も言及していませんでした。
Java Concurrency In Practice(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;
}

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

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

上記のコードは、同期ブロックが完了するまで、リストを変更しようとするすべてのスレッドがリストを変更するのをブロックします。


現時点でこの本を読んでいます...不思議に思っています...そのリストがパブリックではなくプライベートで、putIfAbsentメソッドしかなかった場合、同期(this)で十分でしょうか?手元の問題は、リストがそのListHelper外でも変更できるためです。
dtc 2017

@dtcええ、リストがプライベートであり、クラスの他のどこにもリークされていなければ、リストを同期済みとして変更するクラス内の他のすべてのメソッドにマークを付けていれば、それで十分です。しかし、全体の方法をロックの代わりにすることだけでListは必ずしも同期する必要はありませんコードのログがある場合、パフォーマンスの問題につながる可能性
aarbor

それは理にかなっている。答えてくれてありがとう!tbh、私は私の知識とマルチスレッディングへのアプローチ方法を拡張するのに非常に役立つ本を見つけましたが、それはまた私に全く新しい混乱の世界をもたらしました
dtc

2

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

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

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


1

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

同期ステートメント(§14.17)は、オブジェクトへの参照を計算します。次に、そのオブジェクトに対してロックアクションを実行しようとし、ロックアクションが正常に完了するまでそれ以上処理を進めません。...

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

これらの説明に基づいて、私は以前のほとんどの答えが正しいと思います。同期されたメソッドは、静的メソッドの場合に特に役立つ可能性があります。定義されています。」

編集:私は当初、これらは実際のJava仕様の引用だと思っていました。このページは仕様の概要/説明にすぎないことを明確化


1

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. コンストラクターまたは(静的)初期化ブロック。
    3. 同期アクセスを制御するロックオブジェクトを選択します。

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

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

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

この質問は、スレッドセーフシングルトンダブルチェックロックによるレイジー初期化の違いに関するものだと思います。特定のシングルトンを実装する必要があるときは、常にこの記事を参照します。

さて、これはスレッドセーフシングルトンです。

// 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/


-3

スレッドと同期しています。1)機能しないスレッドでsynchronized(this)を使用しないでください。(this)と同期すると、現在のスレッドがロックスレッドオブジェクトとして使用されます。各スレッドは他のスレッドから独立しているため、同期の調整はありません。2)コードのテストは、Mac上のJava 1.6ではメソッドの同期が機能しないことを示しています。3)synchronized(lockObj)ここで、lockObjは、同期するすべてのスレッドの共通の共有オブジェクトです。4)ReenterantLock.lock()および.unlock()が機能します。これについては、Javaチュートリアルを参照してください。

次のコードは、これらのポイントを示しています。また、ArrayListの代わりに使用されるスレッドセーフなVectorも含まれています。これは、Vectorに追加する多くのスレッドが情報を失うことはなく、ArrayListを使用するスレッドは情報を失う可能性があることを示しています。0)現在のコードは競合状態による情報の損失を示していますA)現在のラベル付きのA行をコメント化し、その上のA行のコメントを外してから実行すると、メソッドはデータを失いますが、そうすべきではありません。B)ステップAを逆にして、Bのコメントを外し、//ブロックを終了します}。次に、データを失うことなく結果を確認するために実行します。C)コメントアウトB、コメントを外します。C。実行します。(これ)で同期すると、予想どおりデータが失われます。すべてのバリエーションを完了する時間はありません。これが役立つことを願っています。(this)で同期する場合、またはメソッドの同期が機能する場合は、テストしたJavaおよびOSのバージョンを明記してください。ありがとうございました。

import java.util.*;

/** RaceCondition - Shows that when multiple threads compete for resources 
     thread one may grab the resource expecting to update a particular 
     area but is removed from the CPU before finishing.  Thread one still 
     points to that resource.  Then thread two grabs that resource and 
     completes the update.  Then thread one gets to complete the update, 
     which over writes thread two's work.
     DEMO:  1) Run as is - see missing counts from race condition, Run severa times, values change  
            2) Uncomment "synchronized(countLock){ }" - see counts work
            Synchronized creates a lock on that block of code, no other threads can 
            execute code within a block that another thread has a lock.
        3) Comment ArrayList, unComment Vector - See no loss in collection
            Vectors work like ArrayList, but Vectors are "Thread Safe"
         May use this code as long as attribution to the author remains intact.
     /mf
*/ 

public class RaceCondition {
    private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
//  private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)

    private String countLock="lock";    // Object use for locking the raceCount
    private int raceCount = 0;        // simple add 1 to this counter
    private int MAX = 10000;        // Do this 10,000 times
    private int NUM_THREADS = 100;    // Create 100 threads

    public static void main(String [] args) {
    new RaceCondition();
    }

    public RaceCondition() {
    ArrayList<Thread> arT = new ArrayList<Thread>();

    // Create thread objects, add them to an array list
    for( int i=0; i<NUM_THREADS; i++){
        Thread rt = new RaceThread( ); // i );
        arT.add( rt );
    }

    // Start all object at once.
    for( Thread rt : arT ){
        rt.start();
    }

    // Wait for all threads to finish before we can print totals created by threads
    for( int i=0; i<NUM_THREADS; i++){
        try { arT.get(i).join(); }
        catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
    }

    // All threads finished, print the summary information.
    // (Try to print this informaiton without the join loop above)
    System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
                MAX*NUM_THREADS, raceList.size(), raceCount );
    System.out.printf("Array lost %,d. Count lost %,d\n",
             MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
    }   // end RaceCondition constructor



    class RaceThread extends Thread {
    public void run() {
        for ( int i=0; i<MAX; i++){
        try {
            update( i );        
        }    // These  catches show when one thread steps on another's values
        catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
        catch( OutOfMemoryError oome ) { System.out.print("O"); }
        }
    }

    // so we don't lose counts, need to synchronize on some object, not primitive
    // Created "countLock" to show how this can work.
    // Comment out the synchronized and ending {, see that we lose counts.

//    public synchronized void update(int i){   // use A
    public void update(int i){                  // remove this when adding A
//      synchronized(countLock){            // or B
//      synchronized(this){             // or C
        raceCount = raceCount + 1;
        raceList.add( i );      // use Vector  
//          }           // end block for B or C
    }   // end update

    }   // end RaceThread inner class


} // end RaceCondition outter class

1
現在のオブジェクトがThreadを拡張するクラスのものでない限り、「(this)」との同期機能し、「現在のスレッドを同期オブジェクトとして使用」しませ。-1
ローンの侯爵
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.