synchronized
キーワードの使用法と意味についていくつか質問があります。
synchronized
キーワードの意味は何ですか?- メソッドはいつすべき
synchronized
ですか? - プログラム的および論理的にはどういう意味ですか?
synchronized
キーワードの使用法と意味についていくつか質問があります。
synchronized
キーワードの意味は何ですか?synchronized
ですか?キーワードは、synchronized
同じ変数、オブジェクト、およびリソースに対して読み取りと書き込みを行うさまざまなスレッドに関するものです。これは Java では些細なトピックではありませんが、Sun からの引用を次に示します。
synchronized
メソッドを使用すると、スレッドの干渉とメモリの一貫性エラーを防ぐための簡単な戦略が可能になります。オブジェクトが複数のスレッドから見える場合、そのオブジェクトの変数へのすべての読み取りまたは書き込みは、同期されたメソッドを介して行われます。
非常に簡単に言えば、同じ「リソース」、たとえば という名前の変数に対して読み取りと書き込みを行う 2 つのスレッドがある場合foo
、これらのスレッドがアトミックな方法で変数にアクセスするようにする必要があります。キーワードがないsynchronized
と、スレッド 1 はスレッド 2 が に行った変更をfoo
認識できないか、さらに悪いことに、半分しか変更されていない可能性があります。これは、論理的に期待するものではありません。
繰り返しますが、これは Java では重要なトピックです。詳細については、SO および Interwebs に関する次のトピックを参照してください。
「Brian Goetz」という名前が頭の中で「同時実行性」という用語に永続的に関連付けられるようになるまで、これらのトピックを調べ続けてください。
さて、理論的な説明は十分だと思うので、このコードを考えてみましょう
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
このsynchronized
キーワードは、複数のスレッドによるコードまたはオブジェクトのブロックへの同時アクセスを防ぎます。のすべてのメソッドはHashtable
であるsynchronized
ため、一度に実行できるスレッドは 1 つだけです。
のような非synchronized
構造体を使用する場合HashMap
は、一貫性エラーを防ぐために、コードにスレッド セーフ機能を構築する必要があります。
synchronized
マルチスレッド環境では、メソッド/ブロックを持つオブジェクトで は、2 つのスレッドがコードのメソッド/ブロックに同時にsynchronized
アクセスできないことを意味します。synchronized
これは、別のスレッドが更新している間、あるスレッドが読み取ることができないことを意味します。
2 番目のスレッドは、最初のスレッドの実行が完了するまで待機します。オーバーヘッドは速度ですが、利点はデータの一貫性が保証されることです。
ただし、アプリケーションがシングル スレッドの場合、synchronized
ブロックはメリットをもたらしません。
このsynchronized
キーワードにより、メソッドに入るときにスレッドがロックを取得するため、メソッドを同時に実行できるスレッドは 1 つだけです (静的メソッドでない限り、特定のオブジェクト インスタンスに対して)。
これは、クラスをスレッドセーフにすることとよく言われますが、これは婉曲表現だと思います。同期によって Vector の内部状態が破損しないように保護されていることは事実ですが、これは通常、Vector のユーザーにとってあまり役に立ちません。
このことを考慮:
if (vector.isEmpty()){
vector.add(data);
}
関連するメソッドは同期されていますが、個別にロックおよびロック解除されているため、タイミングの悪い 2 つのスレッドが 2 つの要素を持つベクトルを作成する可能性があります。
したがって、実際には、アプリケーション コードでも同期する必要があります。
メソッド レベルの同期は、a) 必要がない場合は高価であり、b) 同期が必要な場合は不十分であるため、同期されていない代替 (Vector の場合は ArrayList) が存在します。
最近では、マルチスレッドの問題に対処する多くの巧妙なユーティリティを備えた同時実行パッケージがリリースされました。
サッカー場にあるような回転式改札口のようなものだと考えてください。入りたがっている人々の並行の流れがありますが、改札口ではそれらは「同期」しています。一度に通過できるのは1人だけです。通過したい人はすべてそうしますが、通過できるまで待たなければならない場合があります。
シンクロナイズドキーワードとは?
スレッドは、主にフィールドへのアクセスを共有することによって通信し、フィールドが参照するオブジェクトを参照します。この形式の通信は非常に効率的ですが、スレッド干渉とメモリ整合性エラーの 2 種類のエラーが発生する可能性があります。これらのエラーを防ぐために必要なツールが同期です。
同期されたブロックまたはメソッドは、スレッドの干渉を防ぎ、データの一貫性を確保します。ロックを取得することにより、同期されたブロックまたはメソッド (クリティカル セクション) にアクセスできるのは、常に 1 つのスレッドだけです。他のスレッドは、クリティカル セクションにアクセスするためにロックが解放されるのを待ちます。
メソッドはいつ同期されますか?
synchronized
メソッド定義または宣言に追加すると、メソッドが同期されます。メソッド内で特定のコード ブロックを同期することもできます。
プログラム的および論理的にはどういう意味ですか?
これは、ロックを取得することにより、 1 つのスレッドのみがクリティカル セクションにアクセスできることを意味します。このスレッドがこのロックを解放しない限り、他のすべてのスレッドはロックを取得するまで待機する必要があります。ロックを取得せずにクリティカル セクションに入るアクセス権はありません。
これは魔法ではできません。アプリケーションのクリティカル セクションを特定し、それに応じて保護するのはプログラマの責任です。Java はアプリケーションを保護するためのフレームワークを提供しますが、保護するすべてのセクションの場所と内容はプログラマーの責任です。
詳細はJavaドキュメントページから
固有のロックと同期:
同期は、固有ロックまたはモニター ロックと呼ばれる内部エンティティを中心に構築されます。組み込みロックは、同期の両方の側面で役割を果たします。つまり、オブジェクトの状態への排他的アクセスを強制し、可視性に不可欠な事前発生関係を確立します。
すべてのオブジェクトには固有のロックが関連付けられています。慣例により、オブジェクトのフィールドへの排他的かつ一貫したアクセスを必要とするスレッドは、オブジェクトにアクセスする前にオブジェクトの固有ロックを取得し、それらの操作が完了したら固有ロックを解放する必要があります。
スレッドは、ロックを取得してからロックを解放するまでの間、固有のロックを所有していると見なされます。スレッドが固有ロックを所有している限り、他のスレッドが同じロックを取得することはできません。他のスレッドは、ロックを取得しようとするとブロックされます。
スレッドが固有ロックを解放すると、そのアクションとその後の同じロックの取得との間に発生前の関係が確立されます。
メソッドを同期させると、次の 2 つの効果があります。
まず、同じオブジェクトに対する同期メソッドの 2 つの呼び出しをインターリーブすることはできません。
1 つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトの処理を完了するまでブロック (実行を中断) します。
次に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの先行発生関係が自動的に確立されます。
これにより、オブジェクトの状態の変更がすべてのスレッドに表示されることが保証されます。
で同期に代わる他の方法を探します。
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; } }
count
が のインスタンスである場合、これらSynchronizedCounter
のメソッドを同期させると、次の 2 つの効果があります。
- まず、同じオブジェクトに対する同期メソッドの 2 つの呼び出しをインターリーブすることはできません。1 つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトの処理を完了するまでブロック (実行を中断) します。
- 第 2 に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドのその後の呼び出しとの先行発生関係が自動的に確立されます。これにより、オブジェクトの状態の変更がすべてのスレッドに表示されることが保証されます。
私の理解では、同期は基本的に、コンパイラがメソッドの周りにmonitor.enterとmonitor.exitを書き込むことを意味します。そのため、使用方法によってはスレッドセーフになる場合があります(クラスの動作によっては、スレッドセーフではない同期メソッドを使用してオブジェクトを作成できるということです)。
Java同期
volatile
[概要] =>synchronized
synchronized
Java の block は、マルチスレッドのモニターです。synchronized
同じオブジェクト/クラスを持つブロックは、単一のスレッドでのみ実行でき、他のすべては待機しています。race condition
複数のスレッドが同じ変数を更新しようとする場合に役立ちます。
Java 5
[概要]synchronized
をサポートすることで拡張happens-before
モニターのロック解除 (同期されたブロックまたはメソッドの終了) は、その同じモニターの後続のすべてのロック (同期されたブロックまたはメソッドのエントリ) の前に発生します。
次のステップはjava.util.concurrent