同じクラスで2つのメソッドを同期した場合、それらは同時に実行できますか?


164

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

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

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

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

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

回答:


148

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


1
私はこの質問に付け加えました。両方のメソッドが静的であるとします。methodAはClassを使用して呼び出され、methodBはt1のA.methodA()およびt2のobj.methodB()のようなオブジェクトを使用して呼び出されます。今何が起こるでしょう、彼らはブロックしますか????
amod 2013年

2
@ amod0017:obj.methodB()と同義であるA.methodB()ときmethodB()ですstatic。したがって、はい、それらは(オブジェクトではなく、クラスのモニターで)ブロックします。
NPE 2013年

それを試して戻ってきます。:)
amod

@NPEしたがって、両方のメソッドが静的で、同じオブジェクトの2つのスレッドt1とt2がmethodA()とmethodB()を同時に呼び出そうとしても、1つだけ(たとえばt1)スレッドが実行され、他のスレッドはt1がロックを解放するまで待機する必要があります?
sreeprasad

8
静的メソッドは.classオブジェクトのロックを使用することに注意してください。だからあなたが持っている場合class A {static synchronized void m() {} }。次に、1つのスレッドがnew A().m()それを呼び出してnew A()オブジェクトのロックを取得します。次に、別のスレッドがA.m()それを呼び出すと、スレッドがこの種のロックを所有していないときにオブジェクトのロックが検索されるため、問題は発生しません。ですから、メソッド宣言にもかかわらず、それがactualy アクセスされた 2つの異なるスレッドで同時にし。したがって、オブジェクト参照を使用して静的メソッドを呼び出さないでくださいA.classsynchronized
Alex Semeniuk

113

この例では、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仕様では、多くの場合、このエンティティを単に「モニター」と呼びます。)組み込みロックは、同期の両方の側面で役割を果たします。オブジェクトの状態への排他的アクセスを強制し、可視性に不可欠な発生前の関係を確立します。

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

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


この例では、ロックはクラスAではなく、lockA \ lockBオブジェクトにありますか?これはクラスレベルのロックの例ですか?
Nimrod

2
@Nimrod:Aのインスタンスではなく、lockAおよびlockBオブジェクトをロックしています。ここでは、クラスをロックしているものはありません。クラスレベルのロックとは、クラスオブジェクトのロックを取得することを意味します。static synchronizedまたは、次のようなものを使用しますsynchronized (A.class)
Nathan Hughes

ここでは、ここでの答えを正確に説明するJavaチュートリアルへのリンクです。
Alberto de Paola

18

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

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


クラスの2つの異なるインスタンスに2つのスレッドがある場合、一方のスレッドが1つの同期メソッドを呼び出し、もう一方のスレッドが2番目の同期メソッドを呼び出すように、両方のメソッドを同時に実行できます。私の理解が正しい場合、private final Object lock = new object();同期で使用して、1つのスレッドのみがどちらのメソッドも実行できるようにすることができますか?ありがとう
Yug Singh

13

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

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

と同じです:

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

ロックを次のように定義し、2つの方法で同期ブロックでprivate final Object lock = new Object();使用lockすると、ステートメントはtrueになりますか?IMOはObjectがすべてのオブジェクトの親クラスであるため、スレッドがクラスの異なるインスタンスにある場合でも、同期ブロック内のコードに一度にアクセスできるのは1つだけです。ありがとう。
Yug Singh

クラスで "private final Object lock"を定義して同期すると、クラスインスタンスごとにロックが設定されるため、同期(this)と同じように動作します。
Oleksandr_DJ

はい、オブジェクトはすべてのクラスの親ですが、この場合の「ロック」インスタンスは「所有クラスごとのインスタンス」であるため、同期の「これ」と同じ効果があります。
Oleksandr_DJ

7

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

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

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

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

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

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


6

あなたのコードを以下のものと考えてください:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

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

したがって、メソッドレベルでの同期は、単に同期(これ)を意味します。スレッドがこのクラスのメソッドを実行する場合、実行を開始する前にロックを取得し、メソッドの実行が終了するまでロックを保持します。

しかし、methodA()がまだ実行されている間に、別のスレッドでmethodB()を実行できますか?(同じオブジェクト)

確かに、それは不可能です!

したがって、複数のスレッドが同じオブジェクトで同時に同期されたメソッドをいくつでも実行することはできません。


同じクラスの2つの異なるオブジェクトにスレッドを作成するとどうなりますか?この場合、あるスレッドから1つのメソッドを呼び出し、別のスレッドから他のメソッドを呼び出すと、それらは同時に実行されませんか?
Yug Singh

2
それらは異なるオブジェクトであるためです。つまり、それを防ぎたい場合は、静的メソッドを使用してクラスを同期するか、クラス変数オブジェクトをロックとして使用するか、クラスをシングルトンにすることができます。@Yug Singh
Khosro Makari

4

明確に言うと、オブジェクトレベルのロックと他のクラスレベルのロックがあるため、静的同期メソッドと非静的同期メソッドの両方を同時にまたは同時に実行できる可能性があります。


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() コメント -the出力はmethodBアウト>でmethodAアウト.. methodB>に順番methodAであります * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects()*がコメント化された出力

とを有する出力リレーsynchronizedEffectiveAsMethodsCalledOnSameObject()は コメント -強調表示されたセクション内のスレッド1とThread0によってmethodAの出力が示す同時アクセスを-

* synchronizedEffectiveAsMethodsCalledOnSameObject()*がコメント化された出力

スレッドの数を増やすと、スレッドがさらに目立ちます。


2

いいえ、不可能です。可能であれば、両方のメソッドが同じ変数を同時に更新して、データを簡単に破損する可能性があります。


2

はい、両方のスレッドを同時に実行できます。クラスの2つのオブジェクトを作成する場合、各オブジェクトには1つのロックのみが含まれ、すべての同期されたメソッドにはロックが必要です。したがって、同時に実行する場合は、2つのオブジェクトを作成し、それらのオブジェクト参照を使用して実行してみます。


1

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


0

単一のオブジェクトで共通の同期メソッドを実行する2つの異なるスレッド。オブジェクトは同じであるため、1つのスレッドが同期メソッドでそれを使用すると、ロックを検証する必要があり、ロックが有効になっている場合、このスレッドは待機状態になります。ロックが無効になっている場合、オブジェクトにアクセスできますが、アクセスするとロックが有効になり、実行が完了したときにのみロックが解除されます。別のスレッドが到着すると、ロックを検証します。有効になっているため、最初のスレッドが実行を完了するまで待機し、オブジェクトに設定されたロックを解放します。ロックが解放されると、2番目のスレッドがオブジェクトにアクセスし、実行されるまでロックを有効にします。したがって、実行は同時ではなく、両方のスレッドが1つずつ実行されます。


1
この混乱を適切に区切って大文字にしてください。「ヴァリファイ」という言葉はありません。
ローン侯爵
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.