189

同じクラスで 2 つのメソッドを同期した場合、同じオブジェクトで同時に実行できますか? 例えば:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

methodA()2 つの異なるスレッドで同じオブジェクトを 2 回実行できないことはわかっています。で同じものmethodB()

しかし、実行methodB()中に別のスレッドで実行できmethodA()ますか? (同じオブジェクト)

4

12 に答える 12

175

どちらの方法も同じモニターをロックします。したがって、異なるスレッドから同じオブジェクトに対してそれらを同時に実行することはできません (2 つのメソッドの一方は、他方が終了するまでブロックされます)。

于 2013-03-15T17:36:33.783 に答える
136

この例では、methodA と methodB はインスタンス メソッドです (静的メソッドではありません)。インスタンス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 仕様では、このエンティティを単に「モニター」と呼ぶことがよくあります。) 組み込みロックは、同期の両方の側面で役割を果たします。つまり、オブジェクトの状態への排他的アクセスを強制し、可視性に不可欠な事前発生関係を確立します。

すべてのオブジェクトには固有のロックが関連付けられています。慣例により、オブジェクトのフィールドへの排他的かつ一貫したアクセスを必要とするスレッドは、オブジェクトにアクセスする前にオブジェクトの固有ロックを取得し、それらの操作が完了したら固有ロックを解放する必要があります。スレッドは、ロックを取得してからロックを解放するまでの間、固有のロックを所有していると見なされます。スレッドが固有ロックを所有している限り、他のスレッドが同じロックを取得することはできません。他のスレッドは、ロックを取得しようとするとブロックされます。

ロックの目的は、共有データを保護することです。各ロックが異なるデータ メンバーを保護する場合にのみ、上記のコード例に示すように個別のロックを使用します。

于 2013-03-15T17:39:23.983 に答える
20

Java スレッドは、インスタンス同期 Java メソッドに入るとオブジェクト レベルのロックを取得し、静的同期 Java メソッドに入るとクラス レベルのロックを取得します。

あなたの場合、メソッド(インスタンス)は同じクラスです。そのため、スレッドが Java 同期メソッドまたはブロックに入るたびに、ロック (メソッドが呼び出されるオブジェクト) を取得します。そのため、最初のメソッドが完了して (オブジェクトの) ロックが解除されるまで、同じオブジェクトに対して他のメソッドを同時に呼び出すことはできません。

于 2013-03-15T17:47:10.157 に答える
14

あなたの場合、クラスの同じインスタンスで2つのメソッドを同期しました。したがって、これら 2 つのメソッドは、クラス A の同じインスタンスの異なるスレッドで同時に実行することはできません。ただし、異なるクラス A インスタンスでは実行できます。

class A {
    public synchronized void methodA() {
        //method A
    }
}

以下と同じです:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}
于 2013-03-15T17:49:10.227 に答える
7

オラクルのドキュメントリンクから

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

まず、同じオブジェクトに対する同期メソッドの 2 つの呼び出しをインターリーブすることはできません。1 つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトの処理を完了するまでブロック (実行を中断) します。

次に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの先行発生関係が自動的に確立されます。これにより、オブジェクトの状態の変更がすべてのスレッドに表示されることが保証されます

これはあなたの質問に答えます: 同じオブジェクトで、最初の同期メソッドの実行が進行中の場合、2 番目の同期メソッドを呼び出すことはできません。

このドキュメンテーションページを見て、固有のロックとロックの動作を理解してください。

于 2016-02-21T10:06:21.843 に答える
3

簡単に沈み込まない同期の重要なアイデアは、メソッドが同じオブジェクトインスタンスで呼び出された場合にのみ有効になるということです-回答とコメントですでに強調されています-

以下のサンプルプログラムは、同じことを明確に特定することです-

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() コメント付きの出力 - 出力は methodA in > methodA Out .. methodB in > methodB Out の順です *noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()* コメント付きの出力

およびsynchronizedEffectiveAsMethodsCalledOnSameObject() がコメントされた出力 - 出力は、強調表示されたセクションで Thread1 と Thread0 による methodA の同時アクセスを示しています -

*synchronizedEffectiveAsMethodsCalledOnSameObject()* コメント付きの出力

スレッドの数を増やすと、さらに顕著になります。

于 2019-04-08T18:51:56.227 に答える
1

クラスではなくオブジェクトで同期しています。したがって、同じオブジェクトで同時に実行することはできません

于 2013-03-15T17:37:33.860 に答える