web-dev-qa-db-ja.com

スレッドがデッドロックすることは可能ですか?

Java内のスレッドがそれ自体をデッドロックすることは技術的に可能ですか?)

しばらく前の面接でこれを尋ねられ、不可能だと答えましたが、面接官は可能だと私に言った。残念ながら、私はこのデッドロックを達成する方法について彼の方法を得ることができませんでした。

これは私に考えさせました、そして私が考えることができる唯一の状況はあなたがこれを起こすことができる場所はあなた自身を呼び出すメソッドを含んだRMIサーバープロセスがあるところです。メソッドを呼び出すコード行は、同期されたブロックに配置されます。

それは可能ですか、それともインタビュアーは不正確でしたか?

私が考えていたソースコードはこれらの線に沿っていました(testDeadlockがRMIサーバープロセスで実行されている場合)

public boolean testDeadlock () throws RemoteException {
    synchronized (this) {
        //Call testDeadlock via RMI loopback            
    }
}
60
Pram

呼び出し側のクラスがそれ自体に外部呼び出しを行った場合、JVMはモニターを備えたローカルスレッドのみを追跡します。着信呼び出しにより、元のスレッド自体がデッドロックします。

このコードを実行してアイデアを説明できるはずです

import Java.rmi.*;
import Java.rmi.registry.LocateRegistry;
import Java.rmi.registry.Registry;
import Java.rmi.server.*;

public class DeadlockThreadExample {

    public static interface DeadlockClass extends Remote {
        public void execute() throws RemoteException;
    }

    public static class DeadlockClassImpl extends UnicastRemoteObject implements DeadlockClass {
        private Object lock = new Object();

        public DeadlockClassImpl() throws RemoteException {
            super();
        }

        public void execute() throws RemoteException {
            try {
                System.out.println("execute()::start");

                synchronized (lock) {
                    System.out.println("execute()::Entered Lock");
                    DeadlockClass deadlockClass = (DeadlockClass) Naming.lookup("rmi://localhost/DeadlockClass");
                    deadlockClass.execute();
                }
                System.out.println("execute()::Exited Lock");
            } catch (NotBoundException e) {
                System.out.println(e.getMessage());
            } catch (Java.net.MalformedURLException e) {
                System.out.println(e.getMessage());
            }
            System.out.println("execute()::end");
        }
    }

    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        DeadlockClassImpl deadlockClassImpl = new DeadlockClassImpl();
        Naming.rebind("DeadlockClass", deadlockClassImpl);
        DeadlockClass deadlockClass = (DeadlockClass) Naming.lookup("rmi://localhost/DeadlockClass");
        deadlockClass.execute();
        System.exit(0);
    }
}

プログラムからの出力は次のようになります

execute()::start
execute()::Entered Lock
execute()::start

さらに、スレッドもダンプは次を示します

"main" prio=6 tid=0x00037fb8 nid=0xb80 runnable [0x0007f000..0x0007fc3c]
    at Java.net.SocketInputStream.socketRead0(Native Method)
    at Java.net.SocketInputStream.read(SocketInputStream.Java:129)
    at Java.io.BufferedInputStream.fill(BufferedInputStream.Java:218)
    at Java.io.BufferedInputStream.read(BufferedInputStream.Java:235)
    - locked <0x02fdc568> (a Java.io.BufferedInputStream)
    at Java.io.DataInputStream.readByte(DataInputStream.Java:241)


"RMI TCP Connection(4)-172.17.23.165" daemon prio=6 tid=0x0ad83d30 nid=0x1590 waiting for monitor entry [0x0b3cf000..0x0b3cfce8]
    at DeadlockThreadExample$DeadlockClassImpl.execute(DeadlockThreadExample.Java:24)
    - waiting to lock <0x0300a848> (a Java.lang.Object)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


"RMI TCP Connection(2)-172.17.23.165" daemon prio=6 tid=0x0ad74008 nid=0x15f0 runnable [0x0b24f000..0x0b24fbe8] 
    at Java.net.SocketInputStream.socketRead0(Native Method)
    at Java.net.SocketInputStream.read(SocketInputStream.Java:129)
    at Java.io.BufferedInputStream.fill(BufferedInputStream.Java:218)
    at Java.io.BufferedInputStream.read(BufferedInputStream.Java:235)
    - locked <0x02ffb6d8> (a Java.io.BufferedInputStream)
    at Java.io.DataInputStream.readByte(DataInputStream.Java:241)

これは、スレッドが実際に自分自身をロックしたことを示します

4
Pram

まあ、の定義に基づいて:

デッドロックとは、2つ以上の競合するアクションがそれぞれ他のアクションが完了するのを待っている状況です。

私は答えはノーだと思います-スレッドはそこに無期限に何かを待つことができますが、two競合するアクションがお互いを待っていない限り、定義上はデッドロックではありません。

誰かが私に単一のスレッドが2つのアクションが完了するのを同時に待つ方法を説明しない限り?

PDATE:考えられる唯一の状況は、ある種のメッセージポンプです。スレッドは、無限に待機するように求めるメッセージを処理しますsomethingが発生する場所、実際、somethingはメッセージポンプの別のメッセージによって処理されます。

この(信じられないほど考案された)シナリオは、技術的にはデッドロックと呼ばれる可能性があります。

51
Justin

それは、「デッドロック」が正確に何を意味するかによって異なります。たとえば、何も脈動しないモニターで簡単にwait()を実行できますが、私はそのようなデッドロックを呼び出すことはないと思います。

「自分自身を呼び出すメソッド」の行に沿って考えると、サーバーが特定の数のスレッドしか実行していない場合、それが数えられると、同じサーバーからの応答を待ってすべてがビジーになる可能性があります。 (最も単純な例:サーバーは処理に1つのスレッドのみを使用します。同じサーバーを呼び出す要求ハンドラーを作成すると、同じ要求を処理する前に、ブロックされたスレッドが要求の処理を完了するのを待機します...)これは実際には「同期ブロック」のようなデッドロックではありませんが、注意することは確かに危険です。

編集:この回答を他の定義に適用するには、ここで競合するアクションは「現在の要求を完了する」と「新しい要求を処理する」ことになります。各アクションは、他のアクションが発生するのを待っています。

18
Jon Skeet

たぶん、彼は[〜#〜] lock [〜#〜]自体を意味していました。

synchronized( this )
{
    wait( );
}
10

多分面接官が考えていたのは:

Thread.currentThread().join();

しかし、それはデッドロックとしてカウントされないと主張します。

7
finnw

デッドロックは、複数のスレッド間の相互作用によるリソース不足の形式です。

スレッドがそれ自体をリソースを節約する状態になると、スレッドは livelock と呼ばれます。これはデッドロックに似ていますが、定義上は同じではありません。

ライブロックの例は、ReentrantReadWriteLockの使用です。 OR書き込みでの再入可能であるにもかかわらず、ロックを読み取りから書き込みにアップグレードすることはできません。

public class LiveLock {
    public static void main(String[] args) {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        lock.readLock().lock();
        if (someCondition()) {
            // we want to write without allowing another thread to jump in.
            lock.writeLock().lock();
        }
    }

    private static boolean someCondition() {
        return true;
    }
}

ここでプロセスがブロックされます

"main" #1 prio=5 os_prio=0 tid=0x0000000002a52800 nid=0x550c waiting on condition [0x000000000291f000]
   Java.lang.Thread.State: WAITING (parking)
    at Sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000007162e5e40> (a Java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
    at Java.util.concurrent.locks.LockSupport.park(LockSupport.Java:175)
    at Java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.Java:836)
    at Java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.Java:870)
    at Java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.Java:1199)
    at Java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.Java:943)
    at LiveLock.main(LiveLock.Java:10)

関連する質問です。追加のスレッドを作成せずに、スレッドがデッドロックになる可能性があります。これは、バックグラウンドスレッドがあるため可能です。ファイナライザスレッド。バックグラウンドでユーザーコードを実行できます。これにより、メインスレッドとファイナライザスレッドが互いにデッドロックすることが可能になります。

6
Peter Lawrey

読み取りロックから書き込みロックにアップグレードする(読み取りロックを保持しながら書き込みロックを取得しようとする)と、スレッドが完全にブロックされます。それはデッドロックですか?あなたは裁判官になります...しかし、それがシングルスレッドでエフェクトを作成する最も簡単な方法です。

http://download.Oracle.com/javase/6/docs/api/Java/util/concurrent/locks/ReentrantReadWriteLock.html

6
sjlee

ウィキペディアによると、「デッドロックとは、2つ以上の競合するアクションが、それぞれ他のアクションが完了するのを待っているため、どちらも完了しない状況です。」

...「コンピューターサイエンスでは、コフマンデッドロックは、2つ以上のプロセスがお互いにリソースを解放するのを待っている、または3つ以上のプロセスが循環チェーン内のリソースを待っているという特定の条件を指します。

厳密に言うと、2つ以上がキーワードだと思います。

5
guiding5

正しいとマークされた答え(Pram's)は、技術的に他の人が示唆したようなデッドロックではありません。そのちょうどブロックされました。

Javaでは、Javaの定義に頼ることができます(2つのスレッドのアイデアと一致しています)。 デッドロック自体を検出した場合の場合、最終的な判断はJVMになります。したがって、Pramの例では、スレッドが適切なデッドロックである場合、スレッドは次のようになります。

Deadlock detected
=================

"Negotiator-Thread-1":
  waiting to lock Monitor of com.google.code.tempusfugit.concurrency.DeadlockDetectorTest$Cat@ce4a8a
  which is held by "Kidnapper-Thread-0"

"Kidnapper-Thread-0":
  waiting to lock Monitor of com.google.code.tempusfugit.concurrency.DeadlockDetectorTest$Cash@7fc8b2
  which is held by "Negotiator-Thread-1"

このデッドロック検出は、1.5以降の組み込みロックと1.6以降のLockベースの循環デッドロックで使用できます。

継続的にブロックされたリソース、または少なくとも決して発生しない何かを待っているものはlivelockと呼ばれます。 VM(たとえば)データベースの外部でプロセスがデッドロックする可能性のある同様の問題は完全に可能ですが、この質問には適切ではないと私は主張します。

面接官が可能性を主張する方法についての記事に興味があります...

元の質問への回答では、タンゴに2つ必要ですPramの回答は正しくないため、正しくマークされていないことをお勧めします!;)コールバックするRMIスレッドはブロッキングを引き起こす可能性がありますが、 mainのスレッドとは異なる(RMIサーバーによって管理される)スレッドで実行されます。メインスレッドが明示的に別のスレッドを設定していなくても、2つのスレッドが関係しています。スレッドダンプでの検出の欠如(または、jconsoleで[デッドロックの検出]をクリックした場合)に見られるようなデッドロックはないため、ライブロックとしてより正確に説明されます。

そうは言っても、これらの答えのそれぞれに沿った議論は、インタビュアーとして私を満足させるのに十分でしょう。

3
Toby

私は使用していませんJava以前にシングルスレッドアプリをデッドロックしました。IIRC:ルーチンAがデータを更新して更新しました。ルーチンBも更新する同じデータをロックしました要件の変更により、AはB.を呼び出しました。

もちろん、これはコードを実行しようとしたときに初めて見つけた通常の開発バグにすぎませんが、デッドロック自体はありました。このタイプのデッドロックは、ファイルシステムをサポートするどの言語でも可能だと思います。

2
Loren Pechtel

ReentrantReadWriteLock を使用すると、シングルスレッドのデッドロックに陥ります。書き込みロックは読み取りロックを取得できますが、その逆はできません。以下は無期限にブロックされます。

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    lock.readLock().lock();
    lock.writeLock().lock();
1
mR_fr0g

いいえ、Javaはreentrancyを実装しているためです。ただし、同時実行性とRMIをそのように混同しないでください。スタブでの同期は、内部で同期されるリモートオブジェクトとはまったく異なります。 。

1
b_erb

インタビュアーは正しかった。 スレッドはJCIPに従って自身をデッドロックする可能性があります。しかし、どうやって?

JCIPのセクション2.3.2には、再入可能性に関する次の段落があります。

再入可能性により、ロック動作のカプセル化が容易になり、オブジェクト指向の並行コードの開発が簡素化されます。再入可能ロックがないと、サブクラスが同期されたメソッドをオーバーライドしてからスーパークラスのメソッドを呼び出す、リスト2.7の非常に自然なコードはデッドロックになります。

Synchronizedキーワードのロックは再入可能ロックであるため、スレッドはネストされた方法でロックおよびロック解除できますが、次の例のように非再入可能ロックを使用する場合、私は証拠として書きました。デッドロックが発生します! JCIPによると。

public class SelfDeadLock {


    public static class Father{
        volatile protected int n = 0;
        protected Lock ourLock = new Lock();

        public void writeSth(){
            try {
                ourLock.lock();
                n++;
                System.out.println("Father class: " + n);
            } catch (InterruptedException ex) {
                Logger.getLogger(SelfDeadLock.class.getName()).log(Level.SEVERE, null, ex);
            }
            ourLock.unlock();
        }
    }

    public static class Child extends Father{

        @Override
        public void writeSth() {
            try {
                ourLock.lock();
                n++;
                System.out.println("Child class: " + n);
                super.writeSth();
            } catch (InterruptedException ex) {
                Logger.getLogger(SelfDeadLock.class.getName()).log(Level.SEVERE, null, ex);
            }
            ourLock.unlock();
        }   
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.writeSth();
    }
}
0
Johnny

スレッドが同期ブロックに入ると、現在のスレッドがロックの所有者であるかどうかが確認され、所有者である場合、スレッドは待機せずに続行します。

だからそれは可能だとは思いません。

0
Denis Tulskiy

他のスレッドからメッセージを受信して​​、たとえば終了するように指示できるスレッドを作成します。スレッドにコードを記述して他のスレッドを監視し、終了メッセージを送信して応答を待ちます。スレッドはリスト内で自分自身を見つけ、自分自身にメッセージを送信して終了し、自分自身が終了するのを待ちます。待ち状態よりも着信メッセージを優先するように記述されていない場合...

0
dwarFish

デッドロックという用語の定義を拡張すると、単一のスレッドは、以前に取得した非再入可能ロックでブロックされていることに気付く場合があります。

0
carlsborg

理想的には、古いバージョンの一部の人が 'allegedly' noticeed として本当にJVM自体にバグがない限り、スレッドは「同期ロック」を使用してデッドロック自体を作成してはなりません

0
Gopi

スレッドがデッドロックする方法は次のとおりです。

public class DeadlockMe
{
    public static void main(String[] args)
    {
        DeadlockThing foo = new DeadlockThing();
        synchronized(foo)
        {
            try
            {
                foo.wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}

スレッドはクラス-すべてのクラスのインスタンスを作成し、それを待ちます。スレッドはローカルスコープでオブジェクトを作成したため、他のスレッドがオブジェクトにスレッドをウェイクアップするよう通知する方法はありません。

0
JeremyP

私はこれが古い記事であることを知っています。コードに外部リソースとの相互作用がある場合に発生する可能性のある別の例を次に示します。

データベース接続を開き、transactionAを開始し、更新を開始するスレッドがあります。同じスレッドが別の接続を開き、別のトランザクションBを開始します。ただし、transactionAはまだコミットされておらず、データベーステーブルがロックされているため、transactionBはたまたまこのロックされたテーブルにアクセスするため、待機する必要があります。

最終的に、同じスレッドは複数のデータベース接続を開いたため、それ自体でブロックされます。


アプリケーションには多くのモジュールがあり、スレッドは多くのメソッドを実行できるため、これは私が使用しているアプリケーションで多く発生しました。これらのメソッドは、独自の接続を開きます。さまざまな開発者がコードを作成していたため、コードの呼び出し方法がわからない可能性があり、アプリケーションによって開かれたデータベーストランザクション全体を確認できませんでした。

0
dsum

ここでのコメントは、少なくとも2つのスレッド/アクションが同じリソースで競合している場合に発生する「デッドロック」について平凡ですが、この質問の精神は、特に「再帰的」のコンテキストで、再入可能ロックの必要性について議論することでした"ロック

pythonの例を次に示します(Javaでも概念は同じであると確信しています):RLockをLockに変更すると(つまり、再入可能ロックがロックすると、スレッドがハングします)

import threading

"""
Change RLock to Lock to make it "hang"
"""
lock = threading.Condition(threading.RLock())


def print_list(list):
    lock.acquire()
    if not list:
        lock.release()
        return
    print(list[0])
    print_list(list[1:])
    lock.release()


print_list([1, 2, 3, 4, 5])
0
labheshr