web-dev-qa-db-ja.com

「同期」とはどういう意味ですか?

synchronizedキーワードの使用方法と意味についていくつか質問があります。

  • synchronizedキーワードの意味は何ですか?
  • メソッドはいつsynchronizedにすべきですか?
  • プログラム的および論理的にはどういう意味ですか?
912
Johanna

synchronizedキーワードはすべて、同じ変数、オブジェクト、およびリソースに対して読み書きを行うさまざまなスレッドに関するものです。これはJavaの些細な話題ではありませんが、Sunからの引用です。

synchronizedメソッドはスレッドの干渉やメモリの一貫性エラーを防ぐための簡単な方法を可能にします。オブジェクトが複数のスレッドから見える場合、そのオブジェクトの変数への読み書きはすべて同期メソッドを通して行われます。

非常に小さい一言で言えば: 同じ 'リソース'に対して読み書きしている2つのスレッドがある場合、fooという名前の変数を使用する場合は、これらのスレッドがアトミックで変数にアクセスするようにします方法。 synchronizedキーワードがないと、スレッド1はスレッド2がfooに対して行われた変更を認識できないか、さらに悪いことには半分しか変更されない可能性があります。これはあなたが論理的に期待することではないでしょう。

繰り返しますが、これはJavaの重要なトピックです。詳細については、SOおよびInterwebsに関する以下のトピックを参照してください。

"Brian Goetz" という名前が、頭の中で "concurrency" という用語と完全に関連付けられるまで、これらのトピックを調べ続けてください。

826
Stu Thompson

理論的な説明は十分にあると思いますので、このコードを検討してください。

public class SOP {
    public static void print(String s) {
        System.out.println(s+"\n");
    }
}

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;
    public TestThread(String name,TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }
    @Override
    public void run() {
        theDemo.test(name);
    }
}

public class TheDemo {
    public synchronized void test(String name) {
        for(int i=0;i<10;i++) {
            SOP.print(name + " :: "+i);
            try{
                Thread.sleep(500);
            } catch (Exception e) {
                SOP.print(e.getMessage());
            }
        }
    }
    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1",theDemo);
        new TestThread("THREAD 2",theDemo);
        new TestThread("THREAD 3",theDemo);
    }
}

注:synchronizedは、前のスレッドの実行が終了していない限り、メソッドtest()への次のスレッドの呼び出しをブロックします。スレッドは一度に1つずつこのメソッドにアクセスできます。 synchronizedがなければ、すべてのスレッドがこのメソッドに同時にアクセスできます。

スレッドがオブジェクトの同期メソッド 'test'(ここではオブジェクトは 'TheDemo'クラスのインスタンスです)を呼び出すとき、そのスレッドはそのオブジェクトのロックを取得します。ロックを獲得した方はロックを解除しません。

クラスの静的同期メソッドが呼び出されたときにも同様のことが起こります。スレッドはクラスに関連付けられたロックを取得します(この場合、そのオブジェクトのレベルロックはまだ利用可能であるため、そのクラスのインスタンスの静的でない同期メソッドはすべてのスレッドから呼び出すことができます)。現在ロックを保持しているスレッドによってクラスレベルのロックが解除されない限り、他のスレッドはそのクラスの静的同期メソッドを呼び出すことができません。

同期して出力

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9

同期せずに出力

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9
274
Dheeraj Sachan

synchronizedキーワードは、複数のスレッドによるコードブロックまたはオブジェクトへの同時アクセスを防ぎます。デフォルトでは、Hashtablesynchronizedなので、一度に1つのスレッドしかテーブルにアクセスできません。

HashMapのようなnon-synchronizedコンストラクトを使用する場合は、メモリの一貫性エラーを防ぐためにコード内にスレッドセーフ機能を構築する必要があります。

109
jmort253

synchronizedは、マルチスレッド環境で、synchronizedメソッド/ blockを持つオブジェクトが、2つのスレッドから同時にコードのsynchronized method/blockにアクセスすることを許可しないことを意味します。これは、あるスレッドが別のスレッドがそれを更新している間は読み取ることができないことを意味します。

代わりに、2番目のスレッドは最初のスレッドが実行を完了するまで待機します。オーバーヘッドは速度ですが、利点はデータの整合性が保証されることです。

ただし、アプリケーションがシングルスレッドの場合、synchronizedブロックには利点がありません。

78
Codemwnci

synchronizedキーワードを使用すると、メソッドに入るときにスレッドがロックを取得するため、(静的メソッドでない限り、特定のオブジェクトインスタンスに対して)同時に1つのスレッドしかメソッドを実行できません。

これは、クラスをスレッドセーフにすることとよく呼ばれますが、これは婉曲表現だと思います。同期によってベクターの内部状態が破損するのを防ぐことは事実ですが、これは通常ベクターのユーザーにはそれほど役に立ちません。

このことを考慮:

 if (vector.isEmpty()){
     vector.add(data);
 }

関連するメソッドは同期されていますが、それらは個別にロックおよびロック解除されているため、残念ながら2つのスレッドが2つの要素を持つベクトルを作成できます。

したがって、実際には、アプリケーションコードでも同期する必要があります。

メソッドレベルの同期は、a)必要ないときは高価で、b)同期が必要なときは不十分であるため、非同期の置換があります(Vectorの場合はArrayList)。

最近になって、マルチスレッドの問題を処理する多数の賢いユーティリティと共に、並行処理パッケージがリリースされました。

52
Thilo

概要

Javaの同期キーワードは、スレッドセーフ、つまり複数のスレッドが同じ変数を読み書きする場合に関係があります。
これは直接(同じ変数にアクセスすることによって)または間接に(同じ変数にアクセスする別のクラスを使用するクラスを使用することによって)発生する可能性があります。

Synchronizedキーワードは、複数のスレッドが同じ方法で安全にアクセスできるコードブロックを定義するために使用されます。

もっと深く

構文的には、synchronizedキーワードはパラメータとしてObjectを取り( ロックオブジェクト と呼ばれ)、その後に{ block of code }が続きます。

  • 実行がこのキーワードに遭遇すると、現在のスレッドは ロックオブジェクト を「ロック/取得/所有」し(選択します)、ロックが取得された後で関連するコードブロックを実行しようとします。

  • 同期コードブロック内の変数への書き込みは、同じ ロックオブジェクト を使用して同期コードブロック内のコードを同様に実行する他のすべてのスレッドから見えることが保証されています。

  • 一度に1つのスレッドだけがロックを保持できます。その間、同じ lockオブジェクト を取得しようとする他のすべてのスレッドは待機します(実行を一時停止します)。実行が同期コードブロックを終了すると、ロックは解除されます。

同期メソッド

メソッド定義へのsynchronizedキーワードの追加は、 lockオブジェクト this である同期コードブロックでラップされているメソッド本体全体と同じです(たとえばメソッド) およびClassInQuestion.getClass() (クラスメソッドの場合)

- インスタンスメソッドはstaticキーワードを持たないメソッドです。
- クラスメソッドはstaticキーワードを持つメソッドです。

テクニカル

同期しないと、読み書きがどの順序で行われるかが保証されず、おそらく変数にゴミが残る可能性があります。
(たとえば、あるスレッドによって書き込まれたビットの半分と別のスレッドによって書き込まれたビットの半分が変数になって、どちらのスレッドも書き込みを試みなかった状態になる可能性があります。しかし、両方を組み合わせた混乱です。)

ハードウェアが変数の値をキャッシュしていて、読み取りスレッドには書き込まれた値ではなくキャッシュされた値が表示される可能性があるため、別のスレッドが読み取る前(壁時計時間)に書き込み操作を完了するだけでは不十分です。それ。

結論

したがって、Javaの場合は、スレッド化エラーが発生しないようにするためにJavaメモリモデルに従う必要があります。
言い換えれば、同期化、アトミック操作、あるいはそれらをフードの下で使用するクラスを使用してください。

出典

http://docs.Oracle.com/javase/specs/jls/se8/html/index.html
Java®言語仕様、2015-02-13

24
Gima

あなたがサッカー場で見つけるかもしれないような一種の回転木戸としてそれを考えてください。入りたいと思う人々の平行した蒸気がありますが、回転木戸で彼らは「同期しています」。一度に通過できるのは1人だけです。通り抜けようとする者はすべてうまくいくでしょうが、通り抜けることができるまで待つ必要があるかもしれません。

20
paul

Synchronizedキーワードとは何ですか

スレッドは主にフィールドへのアクセスを共有することによって通信し、参照フィールドが参照するオブジェクトを参照します。この形式の通信は非常に効率的ですが、2種類のエラーを可能にします:スレッド干渉およびメモリ一貫性エラー。これらのエラーを防ぐために必要なツールは同期です。

同期化されたブロックまたはメソッドは、スレッドの干渉を防ぎ、データの整合性を保証します。どの時点でも、ロックを獲得することによって同期化されたブロックまたはメソッド(critical section)にアクセスできるスレッドは1つだけです。他のスレッドはcritical sectionにアクセスするためにロックの解放を待ちます。

メソッドはいつ同期されますか?

メソッド定義または宣言にsynchronizedを追加すると、メソッドは同期されます。特定のコードブロックをメソッド内で同期させることもできます。

文法的および論理的に何を意味しますか?

これは、ロックを獲得することによって、1つのスレッドだけがcritical sectionにアクセスできることを意味します。このスレッドがこのロックを解除しない限り、他のすべてのスレッドはロックを取得するのを待たなければなりません。ロックを取得せずにcritical sectionを入力することはできません。

これは魔法ではできません。アプリケーション内でcritical section(s)を識別し、それに応じて保護することはプログラマの責任です。 Javaはアプリケーションを保護するためのフレームワークを提供しますが、どこでどの部分を保護するかはプログラマの責任です。

Javaのドキュメントからのさらなる詳細 page

組み込みロックと同期:

同期は、組み込みロックまたはモニタロックと呼ばれる内部エンティティを中心に構築されています。組み込みロックは、同期の両方の側面で役割を果たします。オブジェクトの状態への排他的アクセスの強制と、可視性に不可欠な変更前の関係の確立です。

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

スレッドは、ロックを獲得してからロックを解放するまでの間に、固有のロックを所有していると言われます。 スレッドが固有のロックを所有している限り、他のスレッドは同じロックを取得できません。他のスレッドは、ロックを取得しようとするとブロックします。

スレッドが組み込みロックを解放すると、そのアクションとそれ以降の同じロックの獲得との間に、ビフォアビフォア関係が確立されます。

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

まず、同じオブジェクトに対して同期メソッドを2回呼び出してインターリーブすることはできません。

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

2つ目は、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの間に、先代わりの関係が自動的に確立されることです。

これにより、オブジェクトの状態への変更がすべてのスレッドから見えるようになります。

同期化に代わる他の方法を探してください。

Javaで同期(これ)を避けますか?

15
Ravindra babu

これは The Java Tutorials からの説明です。

次のコードを見てください。

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

countSynchronizedCounterのインスタンスである場合、これらのメソッドを同期させると2つの効果があります。

  • まず、同じオブジェクトに対して同期メソッドを2回呼び出してインターリーブすることはできません。あるスレッドがあるオブジェクトに対して同期メソッドを実行しているとき、同じオブジェクトに対して同期メソッドを呼び出す(実行を中断する)他のすべてのスレッドは、そのオブジェクトに対する最初のスレッドが完了するまで実行を中断します。
  • 2つ目は、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの間に、事前に発生前の関係が自動的に確立されることです。これにより、オブジェクトの状態への変更がすべてのスレッドから見えるようになります。
9

Synchronized normal methodと同等のSynchronized statement(これを使用)

class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}

Synchronized static methodSynchronized statementと同等(クラスを使用)

class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}

同期されたステートメント(変数を使用)

class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}

synchronizedには、Synchronized MethodsSynchronized Statementsの両方があります。ただし、Synchronized MethodsSynchronized Statementsに似ているため、Synchronized Statementsを理解する必要があります。

=>基本的に、

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}

synchronizedの理解に役立つ2つの考え方を次に示します。

  • すべてのオブジェクト/クラスには、intrinsic lockが関連付けられています。
  • スレッドがsynchronized statementを呼び出すと、そのintrinsic lockオブジェクトのsynchronized statement'sを自動的に取得し、メソッドが戻ったときに解放します。スレッドがintrinsic lockを所有している限り、NO otherスレッドは同じlock => thread safe。

=> thread Asynchronized(this){// code 1}を呼び出すとき、synchronized(this)とすべてのsynchronized normal method(クラス内)を持つすべてのブロックコード(クラス内)は同じロック。 thread Aロック解除後に実行されます( "// code 1"が終了)。

この動作は、synchronized(a variable){// code 1}またはsynchronized(class)に似ています。

SAME LOCK=> lock(どのメソッドに依存していないか、どのステートメントに依存していないか?)

同期メソッドまたは同期ステートメントを使用しますか?

synchronized statementsの方が拡張性が高いので好きです。たとえば、将来的には、メソッドの一部のみを同期する必要があります。たとえば、2つの同期メソッドがあり、相互に関連するがありませんが、スレッドがメソッドを実行すると、他方がブロックされますメソッド(_synchronized(a variable)を使用することで防ぐことができます)。

ただし、同期方法の適用は単純で、コードは単純に見えます。一部のクラスでは、同期メソッドが1つだけ、またはクラス内のすべての同期メソッドが相互に関連している=> synchronized methodを使用してコードを短く理解しやすくすることができます。

注意

synchronizedにはあまり関係ありません。オブジェクトとクラス、または非静的と静的の違いです)。

  • synchronizedまたは通常のメソッド、またはsynchronized(this)またはsynchronized(non-static variable)を使用すると、各オブジェクトインスタンスに基づいて同期されます。
  • synchronizedまたはstaticメソッド、またはsynchronized(class)またはsynchronized(static variable)を使用すると、クラスに基づいて同期されます

参照

https://docs.Oracle.com/javase/tutorial/essential/concurrency/syncmeth.htmlhttps://docs.Oracle.com/javase/tutorial/essential/concurrency/ locksync.html

お役に立てば幸いです

8
Phan Van Linh

私の理解するところでは、同期は基本的にコンパイラがあなたのメソッドの周りにmonitor.enterとmonitor.exitを書くことを意味します。そのため、使い方によってはスレッドセーフになる可能性があります(クラスの動作によってはスレッドセーフではない同期メソッドを使用してオブジェクトを作成できるという意味です)。

7
Spence

他の答えが欠けているものは1つの重要な側面です: メモリバリア 。スレッド同期は基本的に two part:シリアライゼーションと可視性から成ります。私はみんなに "jvmメモリバリア"をグーグルに勧めます、それはそれが自明で非常に重要なトピックであるので(あなたが複数のスレッドによってアクセスされる共有データを修正するなら)。それが済んだら、明示的な同期の使用を回避するのに役立つJava.util.concurrentパッケージのクラスを検討することをお勧めします。これは、プログラムを単純で効率的に保つのに役立ち、おそらくデッドロックを防ぐのにも役立ちます。

そのような例の1つが ConcurrentLinkedDeque です。 commandパターンと共に 同時キューにコマンドを詰め込むことによって非常に効率的なワーカースレッドを作成することを可能にします - 明示的な同期化、デッドロックの可能性、明示的なsleep()は必要ありません。 ()。

要するに、「メモリの同期」が起こる 暗黙のうちに スレッドを終了したり、揮発性変数を読んだり、モニターのロックを解除したり(同期ブロック/機能を終了したり)します。 (ある意味では「フラッシュ」) all はその特定のアクションの前に行われます。前述の ConcurrentLinkedDeque の場合、ドキュメンテーションは「言います」:

メモリの一貫性の影響:他の並行コレクションと同様に、オブジェクトをConcurrentLinkedDequeに配置する前のスレッド内のアクション before-before 別のスレッドでConcurrentLinkedDequeからその要素をアクセスまたは削除した後のアクション。

この暗黙の振る舞いは経験があまりないほとんどのJavaプログラマーがそれを理由に与えられたものを取るだけなので多少有害な側面です。そして、別の作業負荷がある本番環境で「想定される」ことをJavaが実行していないと、突然このスレッドにつまずいてしまいます - そして並行性の問題をテストするのはかなり困難です。

5
user1050755

同期されたブロックが特定のオブジェクトで使用されている場合、同期は、単一のオブジェクトに関連付けられている場合、複数のスレッドが汚れた読み書きを防ぐことができるということです。より明確にするために、例を挙げましょう。

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

2つのMyRunnableクラスオブジェクトを作成しました。つまり、runnable1はスレッド1と共有され、スレッド3とrunnable2はスレッド2とのみ共有されます。同期せずにt1とt3が起動すると、スレッド1と3の両方が同時にvar値に影響を与えることを示唆するPFB出力。

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

Synchronziedを使用すると、スレッド3はすべてのシナリオでスレッド1が完了するのを待ちます。スレッド1とスレッド3で共有されているrunnable1とスレッド2でのみ共有されているrunnable2の2つのロックが取得されます。

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18
4
paras4all

同期単純とは、2つのスレッドが同時にブロック/メソッドにアクセスできないことを意味します。クラスのブロック/メソッドが同期されていると言うとき、それは一度に1つのスレッドだけがそれらにアクセスできることを意味します。内部でアクセスしようとするスレッドは最初にそのオブジェクトのロックを取得し、このロックが利用できない限り、他のスレッドはクラスのそのインスタンスの同期されたメソッド/ブロックのいずれにもアクセスできません。

別のスレッドが、同期するように定義されていない同じオブジェクトのメソッドにアクセスできることに注意してください。スレッドは以下を呼び出すことでロックを解除できます。

Object.wait()
2
Aniket Thakur