synchronized
キーワードの使用法と重要性について質問があります。
synchronized
キーワードの意味は何ですか?- メソッドはいつすべき
synchronized
ですか? - プログラム的にも論理的にもどういう意味ですか?
synchronized
キーワードの使用法と重要性について質問があります。
synchronized
キーワードの意味は何ですか?synchronized
ですか?回答:
synchronized
キーワードは、異なるスレッドに関するすべてが同じ変数、オブジェクトおよびリソースへの読み書きです。これはJavaの簡単なトピックではありませんが、Sunからの引用です:
synchronized
メソッドは、スレッドの干渉とメモリの一貫性エラーを防ぐための簡単な戦略を可能にします。オブジェクトが複数のスレッドから見える場合、そのオブジェクトの変数へのすべての読み取りまたは書き込みは、同期されたメソッドを介して行われます。
非常に小さな一言で言えば、同じ「リソース」に対して読み取りと書き込みを行う2つのスレッド(たとえば、という変数foo
)がある場合、これらのスレッドがアトミックな方法で変数にアクセスすることを確認する必要があります。なければsynchronized
、キーワード、あなたのスレッド1に対して行った変更のスレッド2が表示されないことがありfoo
、あるいは悪化し、それは半分だけ変更することができます。これはあなたが論理的に期待するものではありません。
繰り返しますが、これはJavaの重要なトピックです。詳細については、SOおよびインターウェブに関する以下のトピックを参照してください。
「ブライアンゲッツ」という名前が脳の「並行性」という用語に永続的に関連付けられるまで、これらのトピックを探索していきます。
まあ、理論的な説明は十分にあると思うので、このコードを検討してください。
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
いますが、メモリの一貫性は無視されます。
synchronized
キーワードは、(それが静的メソッドである場合を除き、指定されたオブジェクトインスタンスの)唯一のスレッドが同時にメソッドを実行できるように、メソッドを入力するときにスレッドがロックを取得させます。
これは、クラスをスレッドセーフにすることと呼ばれますが、これは婉曲表現です。同期によってベクターの内部状態が破損するのを防ぐことは事実ですが、これは通常、ベクターのユーザーにはあまり役立ちません。
このことを考慮:
if (vector.isEmpty()){
vector.add(data);
}
関係するメソッドは同期されていますが、個別にロックおよびロック解除されているため、残念ながら2つのタイミングスレッドが2つの要素を持つベクトルを作成できます。
したがって、実際には、アプリケーションコードでも同期する必要があります。
メソッドレベルの同期は、a)不要な場合は高価であり、b)同期が必要な場合は不十分であるため、非同期の置換(Vectorの場合はArrayList)があります。
最近になって、同時実行パッケージがリリースされ、マルチスレッドの問題を処理する多数の巧妙なユーティリティが追加されました。
Javaの同期キーワードは、スレッドセーフ、つまり複数のスレッドが同じ変数を読み書きする場合に関係しています。
これは、直接(同じ変数にアクセスすることにより)または間接的に(同じ変数にアクセスする別のクラスを使用するクラスを使用することにより)発生する可能性があります。
synchronizedキーワードは、複数のスレッドが同じ変数に安全にアクセスできるコードブロックを定義するために使用されます。
構文的には、synchronized
キーワードはObject
そのままのパラメーター(ロックオブジェクトと呼ばれます)を取り、その後にが続き{ block of code }
ます。
実行がこのキーワードに遭遇すると、現在のスレッドはロックオブジェクトを "lock / acquire / own"(選択)し、ロックが取得された後、関連するコードブロックを実行しようとします。
同期コードブロック内の変数への書き込みはすべて、同じロックオブジェクトを使用して同期コードブロック内のコードを同様に実行する他のすべてのスレッドに確実に表示されます。
一度に1つのスレッドのみがロックを保持できます。その間、同じロックオブジェクトを取得しようとする他のすべてのスレッドは待機します(実行を一時停止します)。ロックは、実行が同期コードブロックを終了するときに解放されます。
synchronized
キーワードをメソッド定義に追加することは、ロックオブジェクトがthis
(たとえば、メソッド)およびClassInQuestion.getClass()
(クラスメソッド)である同期コードブロックにラップされているメソッド本体全体と同じです。
-インスタンスメソッドはstatic
キーワードを持たないメソッドです。
-クラスメソッドはstatic
キーワードを持つメソッドです。
同期しないと、読み取りと書き込みの順序が保証されず、変数が不要なままになる可能性があります。
(たとえば、変数は、1つのスレッドによって書き込まれたビットの半分と別のスレッドによって書き込まれたビットの半分で終了し、どちらのスレッドも書き込みを試みなかった状態に変数を残しますが、両方を組み合わせた混乱です。)
ハードウェアが変数の値をキャッシュしている可能性があるため、別のスレッドが(壁時計時間)前にスレッドで書き込み操作を完了するだけでは十分ではなく、読み取りスレッドは書き込まれた内容ではなく、キャッシュされた値を参照します。それ。
したがって、Javaの場合、スレッド化エラーが発生しないようにするために、Javaメモリモデルに従う必要があります。
言い換えると、内部でそれらを使用する同期、アトミック操作、またはクラスを使用します。
出典
http://docs.oracle.com/javase/specs/jls/se8/html/index.htmlJava®
言語仕様、2015年2月13日
同期キーワードとは何ですか?
スレッドは、主にフィールドとフィールド参照オブジェクトが参照するオブジェクトへのアクセスを共有することで通信します。この形式の通信は非常に効率的ですが、スレッド干渉とメモリ整合性エラーの 2種類のエラーが発生する可能性があります。これらのエラーを防ぐために必要なツールは同期です。
同期されたブロックまたはメソッドは、スレッドの干渉を防ぎ、データの整合性を確保します。いつでも、ロックを取得することにより、同期されたブロックまたはメソッド(クリティカルセクション)にアクセスできるスレッドは1つだけです。他のスレッドは、クリティカルセクションにアクセスするためのロックの解放を待ちます。
メソッドはいつ同期されますか?
メソッドはsynchronized
、メソッド定義または宣言に追加すると同期されます。メソッド内で特定のコードブロックを同期することもできます。
プログラム的にも論理的にもどういう意味ですか?
つまり、ロックを取得してクリティカルセクションにアクセスできるスレッドは1つだけです。このスレッドがこのロックを解放しない限り、他のすべてのスレッドはロックの取得を待機する必要があります。ロックを取得せずにクリティカルセクションにアクセスすることはできません。
これは魔法ではできません。アプリケーションの重要なセクションを特定し、それに応じて保護するのはプログラマの責任です。Javaは、アプリケーションを保護するためのフレームワークを提供しますが、保護するすべてのセクションの場所と内容はプログラマーの責任です。
Javaドキュメントページからの詳細
組み込みロックと同期:
同期は、組み込みロックまたはモニターロックと呼ばれる内部エンティティを中心に構築されます。組み込みロックは、同期の両方の側面で役割を果たします。オブジェクトの状態への排他的アクセスを強制し、可視性に不可欠な発生前の関係を確立します。
すべてのオブジェクトには、固有のロックが関連付けられています。慣例により、オブジェクトのフィールドへの排他的で一貫性のあるアクセスを必要とするスレッドは、オブジェクトにアクセスする前にオブジェクトの固有ロックを取得し、それらが完了したら固有ロックを解放する必要があります。
スレッドは、ロックを取得してからロックを解放するまでの間に、固有のロックを所有していると言います。スレッドが固有のロックを所有している限り、他のスレッドは同じロックを取得できません。他のスレッドは、ロックを取得しようとするとブロックされます。
スレッドが組み込みロックを解放すると、そのアクションとその後の同じロックの取得との間に発生前の関係が確立されます。
メソッドを同期させると、2つの効果があります。
まず、同じオブジェクト上で同期されたメソッドの2つの呼び出しをインターリーブすることはできません。
1つのスレッドがオブジェクトの同期メソッドを実行しているとき、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまでブロックします(実行を中断します)。
第2に、同期メソッドが終了すると、同じオブジェクトの同期メソッドの後続の呼び出しと、前に発生する関係が自動的に確立されます。
これにより、オブジェクトの状態の変更がすべてのスレッドから見えるようになります。
で同期する他の方法を探します。
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
、他のスレッドが取得することができないSAMEロック=>スレッドセーフ。=> thread A
呼び出し時=> 同じロックのためsynchronized(this){// code 1}
、have synchronized(this)
およびall synchronized normal method
(クラス内)のすべてのブロックコード(クラス内)がロックされます。ロック解除後に実行されます( "// code 1"が終了します)。 thread A
この動作は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
または静的メソッドまたは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
それが役に立てば幸い
これは、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つのスレッドがオブジェクトの同期メソッドを実行しているとき、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまでブロックします(実行を中断します)。
- 第2に、同期メソッドが終了すると、同じオブジェクトの同期メソッドの後続の呼び出しと、前に発生する関係が自動的に確立されます。これにより、オブジェクトの状態の変更がすべてのスレッドから見えるようになります。
他にどのような答えが不足していることは、1つの重要な側面である:メモリの障壁。スレッドの同期は、基本的に2つの部分で構成されます:シリアル化と可視性。「jvmメモリーバリア」は簡単ではなく、非常に重要なトピックであるため(複数のスレッドによってアクセスされる共有データを変更する場合)、すべてのユーザーにグーグルすることをお勧めします。それが終わったら、明示的な同期の使用を回避するのに役立つjava.util.concurrentパッケージのクラスを確認することをお勧めします。これにより、プログラムをシンプルかつ効率的に保ち、デッドロックを防ぐことさえできます。
そのような例の1つがConcurrentLinkedDequeです。コマンドパターンと一緒に使用すると、コマンドを並行キューに詰め込むことで非常に効率的なワーカースレッドを作成できます-明示的な同期は不要、デッドロックは不可能、明示的なsleep()は不要、take()を呼び出してキューをポーリングするだけです。
つまり、「メモリ同期」は、スレッドを開始したり、スレッドが終了したり、揮発性変数を読み込んだり、モニターのロックを解除したり(同期されたブロック/関数を残したり)すると、暗黙的に発生します。この「同期」は、ある意味で「フラッシュ」に影響します")その特定のアクションの前に行われたすべての書き込み。前述の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クラスオブジェクトを作成しました。runnable1はスレッド1と共有され、スレッド3&runnable2はスレッド2のみと共有されます。同期が使用されずにt1とt3が開始すると、PFB出力は、スレッド1と3の両方が同時にvar値に影響することを示唆します。スレッド2の場合、varは独自のメモリを持っています。
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の完了を待機します。取得されるロックは2つあり、1つはスレッド1とスレッド3によって共有されるrunnable1にあり、もう1つはスレッド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()
synchronized
Javaのブロックは、マルチスレッド化のモニターです。synchronized
同じオブジェクト/クラスのブロックは単一のスレッドでのみ実行でき、他のすべてのスレッドは待機しています。race condition
複数のスレッドが同じ変数を更新しようとする場合に役立ちます(最初のステップはvolatile
Aboutを使用しています)
Java 5
synchronized
サポートについて拡張happens-before
[概要]
モニターのアンロック(同期ブロックまたはメソッド出口)は、同じモニターの後続の各ロック(同期ブロックまたはメソッドエントリー)の前に発生します。
次のステップは java.util.concurrent