「同期」とはどういう意味ですか?
-
23-08-2019 - |
質問
の使用法と重要性についていくつか質問があります。 synchronized
キーワード。
- の重要性は何ですか
synchronized
キーワード? - メソッドをいつ使用すべきか
synchronized
? - それはプログラム的および論理的に何を意味しますか?
解決
の synchronized
キーワードは、同じ変数、オブジェクト、リソースに対して読み取りと書き込みを行う異なるスレッドに関するものです。これは Java では些細なトピックではありませんが、Sun からの引用を次に示します。
synchronized
方法糸の干渉とメモリの一貫性エラーを防ぐための簡単な戦略を可能にします:オブジェクトが複数のスレッドに表示されている場合、すべてのオブジェクトの変数をすべて読み取りまたは書き込みます。同期されたメソッドを使用して行われます。
とてもとても短い言葉で言うと、 同じ「リソース」に対して読み取りと書き込みを行う 2 つのスレッドがある場合、次の名前の変数を使用するとします。 foo
, 、これらのスレッドがアトミックな方法で変数にアクセスすることを確認する必要があります。なしで synchronized
キーワードを使用すると、スレッド 2 が行った変更がスレッド 1 に表示されない可能性があります。 foo
, 、さらに悪いことに、半分しか変わらない可能性があります。これは論理的に予想されることではありません。
繰り返しますが、これは Java では重要なトピックです。詳細については、SO および Interwebs の次のトピックを参照してください。
名前が付くまでこれらのトピックを探索し続けます 「ブライアン・ゲッツ」 その用語と永久に関連付けられるようになります 「同時実行性」 あなたの脳内で。
他のヒント
さて、理論的な説明はもう十分だと思うので、次のコードを考えてみましょう
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
キーワードが複数のスレッドによってコードまたはオブジェクトのブロックへの同時アクセスを防止します。一度に1つのスレッドしかテーブルにアクセスできるようにデフォルトでは、Hashtable
は、synchronized
です。
non-synchronized
の利用には、HashMap
のように構築し、あなたはメモリの一貫性エラーを防ぐために、あなたのコード内のスレッドの安全機能を構築する必要があります。
synchronized
は、マルチスレッド環境において、オブジェクトを有するsynchronized
法(S)/ブロック(単数または複数)が同時にsynchronized
方法(単数または複数)のコード/ブロック(複数可)にアクセスするには、2つのスレッドをさせないことを意味します。これは、別のスレッドがそれを更新しながら、一つのスレッドを読むことができないことを意味します。
最初のスレッドがその実行を完了するまで、2番目のスレッドが代わりに待機します。オーバーヘッドは速度であるが、利点は、データの一貫性が保証されます。
あなたのアプリケーションはしかしシングルスレッドである場合は、、synchronized
ブロックは利益を提供していません。
synchronized
キーワードは、メソッドに入るとき、スレッドがロックを取得させる。
これは頻繁にクラスのスレッドセーフな作りと呼ばれているが、私は、これは婉曲表現であると言うでしょう。それは同期が壊れるからベクターの内部状態を保護することは事実ですが、これは通常、多くのベクターの使用者を助けていません。
これを考えてみます:
if (vector.isEmpty()){
vector.add(data);
}
これらは個別にロックされ、ロックが解除されるので、としても関与する方法は、二つ残念ながら時限スレッドは、2つの要素を持つベクトルを作成することができ、同期化されます。
効果でだから、あなたは同様にあなたのアプリケーションコードに同期化する必要があります。
あなたがそれを必要としない場合にメソッドレベルの同期)は、高価なであるため、b)の不十分なあなたが同期を必要とするとき、今非同期交換(ベクターの場合ArrayListの)があります。
さらに最近では、同時実行パッケージは、マルチスレッドの問題の世話をする巧妙なユーティリティの数で、リリースされています。
概要
Java の Synchronized キーワードは、スレッド セーフ性、つまり、複数のスレッドが同じ変数を読み書きする場合に関係します。
これは、直接 (同じ変数にアクセスすることによって) または間接的に (同じ変数にアクセスする別のクラスを使用するクラスを使用することによって) 発生する可能性があります。
synchronized キーワードは、複数のスレッドが安全な方法で同じ変数にアクセスできるコード ブロックを定義するために使用されます。
もっと深く
構文的には、 synchronized
キーワードは Object
それはパラメータとして(と呼ばれます) ロックオブジェクト)、その後に { block of code }
.
実行中にこのキーワードが検出されると、現在のスレッドは、そのキーワードを「ロック/取得/所有」(選択) しようとします。 ロックオブジェクト ロックを取得した後、関連するコード ブロックを実行します。
同期されたコード ブロック内の変数への書き込みは、同じコード ブロックを使用して同期されたコード ブロック内のコードを同様に実行する他のすべてのスレッドから見えることが保証されます。 ロックオブジェクト.
ロックを保持できるのは一度に 1 つのスレッドだけであり、その間、他のすべてのスレッドが同じロックを取得しようとします。 ロックオブジェクト 待機します (実行を一時停止します)。ロックは、実行が同期されたコード ブロックを終了すると解放されます。
同期されたメソッド:
追加 synchronized
メソッド定義のキーワードは、同期されたコード ブロックにラップされているメソッド本体全体と同じです。 ロックオブジェクト いる this
(メソッドなど) そして ClassInQuestion.getClass()
(クラスメソッドの場合).
- インスタンスメソッドは、インスタンスメソッドを持たないメソッドです。 static
キーワード。
- クラスメソッドとは、 static
キーワード。
テクニカル
同期がないと、読み取りと書き込みがどの順序で行われるかが保証されず、変数にゴミが残る可能性があります。
(たとえば、変数のビットの半分が 1 つのスレッドによって書き込まれ、ビットの半分が別のスレッドによって書き込まれ、変数はどちらのスレッドも書き込もうとしなかった状態になり、両方のスレッドが組み合わされた混乱した状態になる可能性があります。)
別のスレッドが読み取りを行う前に (実時間で) スレッド内の書き込み操作を完了するだけでは十分ではありません。これは、ハードウェアが変数の値をキャッシュしている可能性があり、読み取りスレッドには、書き込まれた値ではなく、キャッシュされた値が表示されるためです。それ。
結論
したがって、Java の場合は、Java メモリ モデルに従って、スレッド エラーが発生しないようにする必要があります。
言い換えると:同期、アトミック操作、またはそれらを内部で使用するクラスを使用します。
情報源
http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Java® 言語仕様、2015-02-13
サッカー場で見つけるかもしれないあなたのような改札口のようなものと考えてください。そこに取得したい人の並列湯気がありますが、改札口で、彼らは「同期」されています。一度に1人だけを介して取得することができます。介して取得したいすべてのものは行いますが、彼らは、彼らが通過できるようになるまで待つ必要があります。
synchronizedキーワードは何ですか?
スレッドは、フィールドへのアクセスを共有することによって、主に通信し、オブジェクトの参照フィールドを参照してください。通信のこの形式は非常に効率的であるが、可能性のあるエラーの二種類を行います。のねじ干渉及びメモリ一貫性エラーの強いです>。これらのエラーを防止するために必要なツールは、同期です。
同期ブロックまたはメソッドは、スレッドの干渉を防止し、データが一致することを確認します。時間の任意の時点では、一つのスレッドだけがロックを取得することにより、同期ブロックまたはメソッド(のクリティカルセクションの)にアクセスすることができます。他のスレッド(複数可)アクセスにロックの解除のクリティカルセクションのを待ちます。
メソッド同期されますか?
あなたがメソッドの定義または宣言にsynchronized
を追加するときにメソッドが同期化されています。また、この方法ではとのコードの特定のブロックを同期させることができます。
それはプロの文法と論理的に何を意味するのでしょうか?
これは、一つのスレッドだけがロックを取得することで、のクリティカルセクションのアクセスできることを意味します。このスレッドリリースこのロックがない限り、他のすべてのスレッド(複数可)ロックを取得するために待機する必要があります。彼らは出て取得ロックでのクリティカルセクションを入力してアクセスすることはできません。
これは魔法で行うことはできません。これは、アプリケーションでのクリティカルセクション(S)のを確認し、それに応じて、それを守るために、プログラマの責任です。 Javaは、アプリケーションを守るためのフレームワークを提供していますが、どこで、どのようなすべてのセクションが守られるべきことは、プログラマの責任です。
Javaドキュメントのページから詳細
組み込みロックと同期の の
同期は、固有ロックまたはモニタロックとして知られている内部エンティティを中心に構築されています。固有のロックが同期の両方の面で役割を果たしている:オブジェクトの状態と確立への排他的アクセス事前発生視界に不可欠な関係を強化する。
のすべてのオブジェクトは、それの関連付けられた固有ロックを持っています。慣例により、オブジェクトのフィールドへの排他的かつ一貫性のあるアクセスを必要とするスレッドは、それらにアクセスする前に、オブジェクトの固有ロックを取得し、それが彼らと一緒に行っていたときに固有ロックを解除する必要があります。
スレッドは、それがロックを取得し、ロックをリリースしている時間との固有ロックを所有すると言われています。 限りスレッドが固有ロックを所有しているように、他のスレッドが同じロックを取得することができない。それはロックを取得しようとしたときの他のスレッドがブロックされます。
スレッドが固有のロックを解除すると、メソッドを同期作ることは2 効果を持っています>、事前発生関係は、その作用と同じロックの任意の後続の取得との間に確立されます。
同じオブジェクトの同期メソッド二呼び出しがインターリーブする最初のスレッドがオブジェクトに行われるまで、まず、それは不可能です。
ときに1つのスレッド(実行を中断)、オブジェクトの同一の対象ブロックの同期メソッドを呼び出す他のすべてのスレッドを同期化する方法を実行している。
第二に、ときに同期メソッドが終了すると、それは自動的に同じオブジェクトの同期方法の任意の後続の呼び出しで発生し、前の関係を確立します。
これは、オブジェクトの状態への変更がすべてのスレッドから見えることを保証する。
での同期に他の選択肢を探してください
<のhref = "https://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java / 36692190#36692190" >避けJavaで(この)同期には
からの説明はこちら Java チュートリアル.
次のコードを考えてみましょう。
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 つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトの処理を完了するまでブロック (実行を一時停止) します。
- 次に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの間で、事前発生関係が自動的に確立されます。これにより、オブジェクトの状態の変更がすべてのスレッドに表示されることが保証されます。
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 method
に相当 Synchronized 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 Methods
そして Synchronized Statements
. 。しかし、 Synchronized Methods
と類似しています Synchronized Statements
だから私たちは理解する必要があるだけです Synchronized Statements
.
=> 基本的には、
synchronized(object or class) { // object/class use to provides the intrinsic lock
// code
}
理解に役立つ 2 つの考えを次に示します synchronized
- すべてのオブジェクト/クラスには、
intrinsic lock
それに関連付けられています。 - スレッドが
synchronized statement
, を自動的に取得します。intrinsic lock
そのためにsynchronized statement's
オブジェクトを生成し、メソッドが戻ったときにそれを解放します。スレッドがintrinsic lock
, 他にない スレッドが取得できるのは、 同じ ロック => スレッドセーフ。
=> a thread A
呼び出す synchronized(this){// code 1}
=> すべてのブロック コード(クラス内) synchronized(this)
そしてすべて synchronized normal method
(クラス内) がロックされているため、 同じ ロック。後に実行されます thread A
ロックを解除します (「// コード 1」が完了しました)。
この動作は次のようなものです synchronized(a variable){// code 1}
または synchronized(class)
.
同じロック => ロック (どの方法に依存しないのか?)またはどの発言ですか?)
同期メソッドまたは同期ステートメントを使用しますか?
私は好きです synchronized statements
そのほうが拡張性が高いからです。たとえば、将来的にはメソッドの一部を同期するだけで済みます。たとえば、同期されたメソッドが 2 つあり、 何も持っていない 相互に関連性がありますが、スレッドがメソッドを実行すると、他のメソッドがブロックされます(これを防ぐには、 synchronized(a variable)
).
ただし、同期メソッドの適用は単純で、コードもシンプルに見えます。一部のクラスでは、同期メソッドが 1 つだけ、またはクラス内のすべての同期メソッドが相互に関連している => 使用できる synchronized method
コードを短くして理解しやすくするため
注記
(あまり関係ありません synchronized
, 、それはオブジェクトとクラス、または非静的と静的の違いです)。
- 使用するときは
synchronized
通常の方法またはsynchronized(this)
またはsynchronized(non-static variable)
各オブジェクト インスタンスに基づいて同期されます。 - 使用するときは
synchronized
静的メソッドまたはsynchronized(class)
またはsynchronized(static variable)
クラスに基づいて同期されます
参照
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
役に立てば幸いです
基本的に同期私の理解するには、コンパイラはあなたの方法の周りmonitor.enterとmonitor.exitを書くことを意味します。そのようなものとして、それは、それがどのように使われるかに応じて、スレッドセーフであること(私が何を意味するか、あなたのクラスが何をするかに応じて、スレッドセーフされていない同期メソッドを持つオブジェクトを書くことができます)。
他にどのような答えが不足していることは一つの重要な側面です。直列化と可視性:スレッドの同期は、基本的には、の2つのの部品から構成されています。 (あなたが複数のスレッドがアクセスする共有データを変更した場合)、それは非自明な、非常に重要なトピックがあるように私は、「JVMメモリバリア」のためのGoogleに皆をお勧めします。ことをやってしまってから、私は今度は多分デッドロックを防止し、シンプルかつ効率的なプログラムを維持するのに役立ちます明示的な同期を、使用を避けるために役立つjava.util.concurrentパッケージのクラスを見てお勧めします。
その一例である ConcurrentLinkedDeque 。一緒にコマンドパターンは、それが同時キューにコマンドを詰めることにより、高効率のワーカースレッドを作成することができますを - 必要に応じて明示的な同期、無デッドロックの可能な、明示的な睡眠()必要なだけテイクを()を呼び出してキューをポーリングしません。
。要するに:「メモリの同期は、」あなたがスレッドを開始の暗黙のうちにのとき、スレッドが終了すると、あなたは揮発性の変数を読んで起こる、あなたは(synchronizedブロック/機能を残す)モニターのロックを解除するなど、この「同期が」(センス「フラッシュ」で)影響のすべてのは、その特定のアクションの前に行って書き込みます。前述の ConcurrentLinkedDequeする 、ドキュメントは "言う" ます:
メモリー整合性効果:ほかの並行処理コレクションと同様に、 前にオブジェクトを配置するスレッド内のアクション ConcurrentLinkedDeque 起こる-前にアクセスへの後続のアクション 他でConcurrentLinkedDequeからその要素のまたは削除 糸ます。
この暗黙の動作は、それのために与えられたとして、多くの経験なしで、ほとんどのJavaプログラマはちょうどたくさんかかりますので、多少有害な側面です。 Javaは別の作業負荷が生産に行うために、「はず」されて何をしていないされた後、その後、突然、このスレッド上でつまずく - 。それは同時実行の問題をテストすることはかなり困難です。
同期単に単一のオブジェクトに関連付けられている場合、複数のスレッドは、ダーティ・リードを防止し、同期ブロックは、特定の目的で使用されている場合に書くことができることを意味します。あなたはより多くの明快さを与えるために、例を取ることができます:
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クラスのオブジェクトを作成して、スレッド1とスレッド3&runnable2と共有されてrunnable1は、スレッド2のみで共有されています。 今T1とT3は、スレッド1および3の両方が同時にスレッド2ため、varは自身のメモリを有し、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、すべてのシナリオで完了するため、スレッド1を待っているスレッド3を使用しました。取得された2つのロック、スレッド1によって共有runnable1と糸3上の唯一のスレッド2によって共有runnable2上の別があります。
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
には2つのスレッドが同時にブロック/メソッドにアクセスすることができない簡単な手段を同期。私たちは、クラスの任意のブロック/メソッドを言うとき、それは一度に1つのスレッドしかそれらにアクセスすることができることを意味同期されます。それは最初にそのオブジェクトのロックを取得している限り、このロックが使用できないように他のスレッドは、クラスのインスタンスの同期化方法/ブロックのいずれかにアクセスすることはできませんアクセスしようとする内部スレッド
別のスレッドが同期されるように定義されていない同じオブジェクトのメソッドにアクセスすることができます。スレッドが呼び出すことでロックを解除することができます。
Object.wait()
されて同期させるために使用されているJavaでのキーワードは、メモリの矛盾とねじ干渉エラーを回避するために、マルチスレッド環境での関係の前に起こります。