同じクラスで2つのメソッドを同期した場合、同時に実行できます同じオブジェクトで?例えば:
class A {
public synchronized void methodA() {
//method A
}
public synchronized void methodB() {
// method B
}
}
2つの異なるスレッドの同じオブジェクトでmethodA()
を2回実行できないことを知っています。 methodB()
でも同じです。
しかし、methodB()
の実行中に別のスレッドでmethodA()
を実行できますか? (同じオブジェクト)
どちらの方法も同じモニターをロックします。したがって、異なるスレッドの同じオブジェクトでそれらを同時に実行することはできません(2つのメソッドの一方は、他方が終了するまでブロックされます)。
例では、methodAとmethodBはインスタンスメソッドです(静的メソッドとは対照的です)。インスタンスメソッドにsynchronized
を置くことは、スレッドがメソッド内のコードの実行を開始する前に、メソッドが呼び出されるオブジェクトインスタンスのロック(「固有のロック」)を取得する必要があることを意味します。
Synchronizedとマークされた2つの異なるインスタンスメソッドがあり、異なるスレッドが同じオブジェクトでそれらのメソッドを同時に呼び出している場合、それらのスレッドは同じロックを求めて競合します。 1つのスレッドがロックを取得すると、他のすべてのスレッドは、そのオブジェクトのすべての同期インスタンスメソッドからシャットアウトされます。
2つのメソッドを同時に実行するには、次のように異なるロックを使用する必要があります。
class A {
private final Object lockA = new Object();
private final Object lockB = new Object();
public void methodA() {
synchronized(lockA) {
//method A
}
}
public void methodB() {
synchronized(lockB) {
//method B
}
}
}
同期ブロック構文では、ブロックに入るために実行中のスレッドが固有のロックを取得する必要がある特定のオブジェクトを指定できます。
理解しておくべき重要なことは、個々のメソッドに「同期」キーワードを設定している場合でも、コア概念は舞台裏の固有のロックであるということです。
Javaチュートリアル が関係を説明する方法は次のとおりです。
同期は、固有ロックまたはモニターロックと呼ばれる内部エンティティを中心に構築されます。 (API仕様では、このエンティティを単に「モニター」と呼ぶことがよくあります。)固有のロックは、同期の両方の側面で役割を果たします。
すべてのオブジェクトには、固有のロックが関連付けられています。慣例により、オブジェクトのフィールドへの排他的で一貫したアクセスを必要とするスレッドは、アクセスする前にオブジェクトの固有ロックを取得し、それらの処理が完了したら固有ロックを解放する必要があります。スレッドは、ロックを取得してからロックを解放するまでの間に固有のロックを所有すると言われています。スレッドが固有のロックを所有している限り、他のスレッドは同じロックを取得できません。他のスレッドは、ロックを取得しようとするとブロックします。
ロックの目的は、共有データを保護することです。上記のサンプルコードに示すように、各ロックが異なるデータメンバーを保護している場合にのみ、個別のロックを使用します。
Javaスレッドはinstance synchronized Javaメソッドに入るとオブジェクトレベルロックを取得し、クラスレベルロックstatic synchronized Javaメソッドに入るとき。
あなたの場合、メソッド(インスタンス)は同じクラスです。そのため、スレッドがJava同期メソッドまたはブロックに入ると、ロック(メソッドが呼び出されるオブジェクト)を取得します。そのため、最初のメソッドが完了してlock(on object)が解放されるまで、同じオブジェクトで他のメソッドを同時に呼び出すことはできません。
あなたの場合、クラスの同じインスタンスで2つのメソッドを同期しました。したがって、これら2つのメソッドは、クラスAの同じインスタンスの異なるスレッドで同時に実行することはできません。しかし、異なるクラスAインスタンスで実行することはできます。
class A {
public synchronized void methodA() {
//method A
}
}
次と同じです:
class A {
public void methodA() {
synchronized(this){
// code of method A
}
}
}
Oracleのドキュメントから link
メソッドを同期させると、次の2つの効果があります。
まず、同じオブジェクトで同期メソッドを2回呼び出してインターリーブすることはできません。 1つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、オブジェクトで最初のスレッドが完了するまでブロック(実行を中断)します。
第二に、同期メソッドが終了すると、同じオブジェクトの同期メソッドの以降の呼び出しとの発生前関係を自動的に確立します。これにより、オブジェクトの状態への変更がすべてのスレッドから見えるようになります。
これはあなたの質問に答えます:同じオブジェクトで、最初の同期メソッドの実行が進行中の場合、2番目の同期メソッドを呼び出すことはできません。
このドキュメントを参照してください page 固有のロックとロックの動作を理解してください。
コードを次のように考えてください。
class A {
public void methodA() {
synchronized(this){
//method A body
}
}
public void methodB() {
synchronized(this){
// method B body
}
}
したがって、メソッドレベルでの同期は、単にsynchronized(this)を意味します。スレッドがこのクラスのメソッドを実行する場合、実行を開始する前にロックを取得し、メソッドの実行が終了するまでロックを保持します。
しかし、methodA()の実行中に別のスレッドでmethodB()を実行できますか? (同じオブジェクト)
確かに、それは不可能です!
そのため、複数のスレッドが同じオブジェクトで同期メソッドを同時に実行することはできません。
わかりやすく言うと、オブジェクトレベルのロックと他のクラスレベルのロックがあるため、静的同期メソッドと非静的同期メソッドの両方が同時にまたは同時に実行される可能性があります。
クラスではなくオブジェクトで同期しています。したがって、同じオブジェクトで同時に実行することはできません
いいえ、不可能です。可能であれば、両方の方法で同じ変数を同時に更新し、データを簡単に破損する可能性があります。
はい、両方のスレッドを同時に実行できます。各オブジェクトにはロックが1つしか含まれておらず、すべての同期メソッドにはロックが必要であるため、クラスの2つのオブジェクトを作成する場合。したがって、同時に実行する場合は、2つのオブジェクトを作成してから、それらのオブジェクト参照を使用して実行してみてください。
key同期のアイデアは簡単には沈みませんが、メソッドがsame objectインスタンスで呼び出された場合にのみ効果があります-回答で既に強調表示されていますとコメント-
以下のサンプルプログラムは、同じものを明確に特定するためのものです-
public class Test {
public synchronized void methodA(String currentObjectName) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}
public synchronized void methodB(String currentObjectName) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}
public static void main(String[] args){
Test object1 = new Test();
Test object2 = new Test();
//passing object instances to the runnable to make calls later
TestRunner runner = new TestRunner(object1,object2);
// you need to start atleast two threads to properly see the behaviour
Thread thread1 = new Thread(runner);
thread1.start();
Thread thread2 = new Thread(runner);
thread2.start();
}
}
class TestRunner implements Runnable {
Test object1;
Test object2;
public TestRunner(Test h1,Test h2) {
this.object1 = h1;
this.object2 = h2;
}
@Override
public void run() {
synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
//noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}
// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
try {
object1.methodA("object1");
object1.methodB("object1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
try {
object1.methodA("object1");
object2.methodB("object2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
メソッドが異なるオブジェクトインスタンスで呼び出された場合、同時アクセスがどのように許可されるかが予想どおりであることに注意してください。
出力noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()commented-出力は、methodA in> methodA Out .. methodB in> methodB Out
およびsynchronizedEffectiveAsMethodsCalledOnSameObject()commentedを使用した出力-強調表示されたセクションのThread1およびThread0によるmethodAへの同時アクセスを示します-
スレッドの数を増やすと、スレッドはさらに顕著になります。