web-dev-qa-db-ja.com

同期メソッドが別の非同期メソッドを呼び出す場合、非同期メソッドにロックがありますか

Javaでは、同期されたメソッドに非同期のメソッドへの呼び出しが含まれる場合、別のメソッドが同時に非同期のメソッドにアクセスできますか?基本的に私が求めているのは、同期メソッド内のすべてがロックされていることです(他の同期メソッドへの呼び出しを含む)?本当にありがとう

38
dido

同期メソッドが別の非同期メソッドを呼び出す場合、非同期メソッドにロックがあります

はいといいえ。

synchronizedメソッドを使用している場合、他のスレッドによるsynchronizedである他のメソッドへの呼び出しはロックされます。ただし、他のスレッドによる非同期メソッドへの呼び出しはnotロックされており、誰でも同時に呼び出すことができます。

_public synchronized void someSynchronizedMethod() {
    ...
    someNonSynchronizedMethod();
    ...
}

// anyone can call this method even if the someSynchronizedMethod() method has
// been called and the lock has been locked
public void someNonSynchronizedMethod() {
   ...
}
_

また、someSynchronizedMethod()を呼び出したが、たまたまsomeNonSynchronizedMethod()メソッド内にある場合でも、ロックを保持します。ロックは、同期ブロックを入力すると有効になり、そのメソッドを終了すると無効になります。他のあらゆる非同期メソッドを呼び出すことができますが、それらは引き続きロックされます。

しかし、あなたはあなたの質問で2つの異なることを求めています:

Javaでは、同期されたメソッドに非同期のメソッドへの呼び出しが含まれている場合、別のメソッドが同時に非同期のメソッドにアクセスできますか?

はい。他のメソッドは非同期メソッドにアクセスできます。

基本的に私が求めているのは、同期メソッドのすべてがロックされていることです(他の同期メソッドの呼び出しを含む)?

ええ、はい。 synchronizedメソッドの他の呼び出しはロックされています。ただし、非同期メソッドはロックされません。

また、メソッドがstaticの場合、ロックはClass内のClassLoaderオブジェクトにあることに注意してください。

_// this locks on the Class object in the ClassLoader
public static synchronized void someStaticMethod() {
_

メソッドがインスタンスメソッドの場合、ロックはクラスのインスタンスにあります。

_// this locks on the instance object that contains the method
public synchronized void someInstanceMethod() {
_

これら2つのケースには2つの異なるロックがあります。

最後に、synchronizedインスタンスメソッドを処理する場合、クラスの各インスタンスがロックされます。これは、異なるinstancesで2つのスレッドが同じsynchronizedメソッドに同時に存在する可能性があることを意味します。ただし、2つのスレッドが同じインスタンスのsynchronizedメソッドを操作しようとすると、一方が他方を終了するまでブロックします。

56
Gray

スレッドAが非同期メソッドM2を呼び出す同期メソッドM1を呼び出す場合、スレッドBはブロックせずにM2を呼び出すことができます。

同期メソッドは、呼び出されたオブジェクトの固有ロックを取得および解放します。これがブロックする理由です。非同期メソッドはロックを取得しようとしません(コードで明示的に行われない限り)。

したがって、M2の相互排除も保証する必要がある場合は、呼び出し元(M1など)が同期されているかどうかに関係なく、M2を同期させる必要があります。

3
Adam Zalcman

ロックはスレッドに属していません。ロックは、実際にはオブジェクト(またはクラスレベルロックの場合はクラス)に属し、スレッドは同期コンテキスト内でオブジェクト(またはクラスレベルロックの場合はクラス)のロックを取得します。ここで、上記で説明したように、Javaにはロックの伝播はありません。小さなデモを次に示します。

パブリッククラスTestThread {

/**
 * @param args
 * @throws InterruptedException 
 */
public static void main(String[] args) throws InterruptedException {
    // TODO Auto-generated method stub
    ThreadCreator1 threadCreator1 = new ThreadCreator1();
    ThreadCreator2 threadCreator2 = new ThreadCreator2();

    Thread t1 = new Thread(threadCreator1,"Thread 1");
    Thread t3 = new Thread(threadCreator1,"Thread 3");
    Thread t2 = new Thread(threadCreator2,"Thread 2");

    t1.start();
    Thread.sleep(2000);
    t3.start();

}

}

パブリッククラスThreadCreator1はRunnable {

private static final Task task= new Task();
private static final Task2 task2= new Task2();

@Override

public void run() {

    try {

        if(Thread.currentThread().getName().equals("Thread 1"))
            task.startTask2(task2);
        if(Thread.currentThread().getName().equals("Thread 3"))
            task2.startTask();

    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    // TODO Auto-generated method stub

    /**/

    }
}

パブリッククラスTask {

public static final Task task = new Task();
public static List<String> dataList = new ArrayList<String>();
ReentrantLock lock =  new ReentrantLock();



public  void startTask2(Task2 task2) throws InterruptedException
{

    try{

        lock.lock();
        //new Task2().startTask();
        task2.startTask();
    }
    catch(Exception e)
    {

    }
    finally{
        lock.unlock();
    }
}

}

パブリッククラスTask2 {

ReentrantLock lock = new ReentrantLock();
public  void startTask() throws InterruptedException
{

    try{
        //lock.lock();
        for(int i =0 ;i< 10;i++)
    {
        System.out.println(" *** Printing i:"+i+" for:"+Thread.currentThread().getName());
        Thread.sleep(1000);
    }
    }
    catch(Exception e)
    {

    }
    /*finally
    {
        lock.unlock();
    }*/
}

}

ここで再入可能ロックを使用しました。上記のコードを実行すると、スレッド1とスレッド3の間にインターリーブが行われますが、Task2クラスのロック部分のコメントが解除されている場合、インターリーブは行われず、最初にロックを取得したスレッドが最初に完全に完了し、次にロックを解除すると、他のスレッドが続行できます。

2
user2398545

ロックはthreadに属し、メソッド(より正確には、そのスタックフレーム)に属しません。同期されたメソッドを持っている場合、メソッドの本体が開始する前にスレッドがロックを所有し、後でそれを解放することが保証されているのはまさにそのためです。

別のスレッドは、まだ2番目の非同期メソッドを呼び出すことができます。非同期メソッドは、いつでも任意のスレッドから呼び出すことができます。

0
yshavit