JavaA2Z

KAB-studio > プログラミング > JavaA2Z > デッドロックとは

デッドロック

日本語 死鍵、膠着状態
英語 deadlock
ふりがな でっどろっく
フリガナ デッドロック

解説

マルチスレッドにおいて、複数のスレッドが互いのスレッド排他してしまいプログラムが停止すること。
複数のスレッドが、synchronizedブロック等によって複数の変数を対象に排他処理をう場合に発生する。
単純な例としては、スレッドAとスレッドB、ロック変数1とロック変数2がある場合に、以下のように処理をうとデッドロックする。
 
1. スレッドAが変数1をロックする。
2. スレッドBが変数2をロックする。
3. スレッドAが変数2をロックしようとしたらすでにされていたので一時停止。
4. スレッドBが変数1をロックしようとしたらすでにされていたので一時停止。
 
このように、同期を取るメソッドそれぞれにおいて、複数のロック変数の「ロック順」が異なる場合に発生する。スレッドAが「変数1→変数2」の順でロックし、スレッドBが「変数2→変数1」の順でロックするため、同時にロックが掛かると停止してしまう。
 
実際には、このように分かりやすくはない。
順番にロックを掛ける際に、ロックの間隔が非常に短い場合、ほぼ同時にロックが掛かるためである。
スレッドAが変数1をロックし、その後変数2をロックする前にスレッドBが変数2をロックしてしまうために発生するのであって、スレッドAが変数1と変数2をほぼ同時にロックしてしまえば、スレッドBの方が変数2のロック解除を待つことになるからである。
 
問題は、CPUの処理順等によって、「確率的」に発生してしまう可能性があるためである。
CPUの処理順の変更や、他のアプリケーションの影響、OSの状態等によって、ロックの間隔が空いてしまい、他のスレッドが先にロックを掛けてしまうと、デッドロックが発生してしまう。
これらの要因は常に発生するわけではないため、入念にテストをわないと発見できない。また、発見できても原因の特定や解決そのものが難しいことも多い。
 
デッドロックは、ロック用の変数がひとつであれば発生しないため、そういったシンプルなプログラムを組むよう心がけることが重要だろう。

参考サイト


(KAB-studioからのおしらせです)

サンプルプログラム(とか)サンプルを別ウィンドウで表示サンプルをクリップボードへコピー(WindowsでIEの場合のみ)

// Sample.java
public class Sample
{
    public static void main( String[] args )
    {
        try
        {
            // データクラス。
            DataClass data1 = new DataClass();
            DataClass data2 = new DataClass();

            // 別スレッドの方を呼び出します。
            OtherThread thread = new OtherThread( data1, data2 );
            thread.start();

            // 2秒待ちます。OtherThreadの方を先に
            // 実行するためです。
            Thread.sleep( 2 * 1000 );

            // こちらでも。
            System.out.println( "Sample開始" );
            // もうひとつのsynchronizedブロックを持つメソッドを呼び出します。
            SynchronizeClass synchronizeClass = new SynchronizeClass();
            synchronizeClass.methodHasSynchronizedBlockB( "Sample", data1, data2 );
            System.out.println( "Sample終了" );
            // OtherThread開始
            // A : data1をロックします
            // A : data1ロック開始
            // Sample開始
            // B : data2をロックします
            // B : data2ロック開始
            // A : data2をロックします
            // B : data1をロックします

            // ここでデッドロックが発生します。
            // 「ロックします」のところで、すでにロックが掛かっていると一時停止します。
            // そのため、
            // 
            // ・Aがdata1をロックする。
            // ・Bがdata2をロックする。
            // ・Aがdata2をロックしようとしたらすでにされていたので一時停止。
            // ・Bがdata1をロックしようとしたらすでにされていたので一時停止。
            // 
            // となり、デッドロックが発生します。
        }
        catch( InterruptedException e )
        {
            // sleep()メソッドが途中で中断されると
            // InterruptedException例外が投げられます。
            // 滅多にないですが。
            e.printStackTrace();
        }
    }
}

/**
*   別スレッドとして実行するためのクラス。
*/
class OtherThread extends Thread
{
    /** データクラス。 */
    private DataClass data1;
    private DataClass data2;

    /** コンストラクタ。 */
    public OtherThread( DataClass data1, DataClass data2 )
    {
        this.data1 = data1;
        this.data2 = data2;
    }
    
    /**
    *   Threadクラスのrun()メソッドを
    *   オーバーライドしたメソッド。このメソッドが
    *   別スレッドとして呼び出されます。
    */
    public void run()
    {
        System.out.println( "OtherThread開始" );
        // synchronizedブロックを持つメソッドを呼び出します。
        SynchronizeClass synchronizeClass = new SynchronizeClass();
        synchronizeClass.methodHasSynchronizedBlockA( "OtherThread", data1, data2 );
        System.out.println( "OtherThread終了" );
    }
}

/**
*   同期処理テスト用クラス。
*/
class SynchronizeClass
{
    /**
    *   synchronizedブロックを持つメソッド
    */
    public void methodHasSynchronizedBlockA( String name, DataClass data1, DataClass data2 )
    {
        try
        {
            System.out.println( "A : data1をロックします" );
            synchronized( data1 )
            {
                System.out.println( "A : data1ロック開始" );
                // 5秒待ちます。
                Thread.sleep( 5 * 1000 );

                System.out.println( "A : data2をロックします" );
                synchronized( data2 )
                {
                    System.out.println( "A : data2ロック開始" );

                    System.out.println( "A : data2ロック終了" );
                }
                System.out.println( "A : data2のロックを解放しました" );

                System.out.println( "A : data1ロック終了" );
            }
            System.out.println( "A : data1のロックを解放しました" );
        }
        catch( InterruptedException e )
        {
            // sleep()メソッドが途中で中断されると
            // InterruptedException例外が投げられます。
            // 滅多にないですが。
            e.printStackTrace();
        }
    }

    /**
    *   もうひとつのsynchronizedブロックを持つメソッド
    */
    public void methodHasSynchronizedBlockB( String name, DataClass data1, DataClass data2 )
    {
        try
        {
            System.out.println( "B : data2をロックします" );
            synchronized( data2 )
            {
                System.out.println( "B : data2ロック開始" );
                // 5秒待ちます。
                Thread.sleep( 5 * 1000 );

                System.out.println( "B : data1をロックします" );
                synchronized( data1 )
                {
                    System.out.println( "B : data1ロック開始" );

                    System.out.println( "B : data1ロック終了" );
                }
                System.out.println( "B : data1のロックを解放しました" );

                System.out.println( "B : data2ロック終了" );
            }
            System.out.println( "B : data2のロックを解放しました" );
        }
        catch( InterruptedException e )
        {
            // sleep()メソッドが途中で中断されると
            // InterruptedException例外が投げられます。
            // 滅多にないですが。
            e.printStackTrace();
        }
    }
}

/**
*   データクラス。
*/
class DataClass
{
}
// Sample.java
public class Sample
{
    public static void main( String[] args )
    {
        try
        {
            // データクラス。
            DataClass data1 = new DataClass();
            DataClass data2 = new DataClass();

            // 別スレッドの方を呼び出します。
            OtherThread thread = new OtherThread( data1, data2 );
            thread.start();

            // 2秒待ちます。OtherThreadの方を先に
            // 実行するためです。
            Thread.sleep( 2 * 1000 );

            // こちらでも。
            System.out.println( "Sample開始" );
            // もうひとつのsynchronizedブロックを持つメソッドを呼び出します。
            SynchronizeClass synchronizeClass = new SynchronizeClass();
            synchronizeClass.methodHasSynchronizedBlockB( "Sample", data1, data2 );
            System.out.println( "Sample終了" );
            // OtherThread開始
            // A : data1をロックします
            // A : data1ロック開始
            // Sample開始
            // B : data2をロックします
            // B : data2ロック開始
            // A : data2をロックします
            // B : data1をロックします

            // ここでデッドロックが発生します。
            // 「ロックします」のところで、すでにロックが掛かっていると一時停止します。
            // そのため、
            // 
            // ・Aがdata1をロックする。
            // ・Bがdata2をロックする。
            // ・Aがdata2をロックしようとしたらすでにされていたので一時停止。
            // ・Bがdata1をロックしようとしたらすでにされていたので一時停止。
            // 
            // となり、デッドロックが発生します。
        }
        catch( InterruptedException e )
        {
            // sleep()メソッドが途中で中断されると
            // InterruptedException例外が投げられます。
            // 滅多にないですが。
            e.printStackTrace();
        }
    }
}

/**
*   別スレッドとして実行するためのクラス。
*/
class OtherThread extends Thread
{
    /** データクラス。 */
    private DataClass data1;
    private DataClass data2;

    /** コンストラクタ。 */
    public OtherThread( DataClass data1, DataClass data2 )
    {
        this.data1 = data1;
        this.data2 = data2;
    }
    
    /**
    *   Threadクラスのrun()メソッドを
    *   オーバーライドしたメソッド。このメソッドが
    *   別スレッドとして呼び出されます。
    */
    public void run()
    {
        System.out.println( "OtherThread開始" );
        // synchronizedブロックを持つメソッドを呼び出します。
        SynchronizeClass synchronizeClass = new SynchronizeClass();
        synchronizeClass.methodHasSynchronizedBlockA( "OtherThread", data1, data2 );
        System.out.println( "OtherThread終了" );
    }
}

/**
*   同期処理テスト用クラス。
*/
class SynchronizeClass
{
    /**
    *   synchronizedブロックを持つメソッド
    */
    public void methodHasSynchronizedBlockA( String name, DataClass data1, DataClass data2 )
    {
        try
        {
            System.out.println( "A : data1をロックします" );
            synchronized( data1 )
            {
                System.out.println( "A : data1ロック開始" );
                // 5秒待ちます。
                Thread.sleep( 5 * 1000 );

                System.out.println( "A : data2をロックします" );
                synchronized( data2 )
                {
                    System.out.println( "A : data2ロック開始" );

                    System.out.println( "A : data2ロック終了" );
                }
                System.out.println( "A : data2のロックを解放しました" );

                System.out.println( "A : data1ロック終了" );
            }
            System.out.println( "A : data1のロックを解放しました" );
        }
        catch( InterruptedException e )
        {
            // sleep()メソッドが途中で中断されると
            // InterruptedException例外が投げられます。
            // 滅多にないですが。
            e.printStackTrace();
        }
    }

    /**
    *   もうひとつのsynchronizedブロックを持つメソッド
    */
    public void methodHasSynchronizedBlockB( String name, DataClass data1, DataClass data2 )
    {
        try
        {
            System.out.println( "B : data2をロックします" );
            synchronized( data2 )
            {
                System.out.println( "B : data2ロック開始" );
                // 5秒待ちます。
                Thread.sleep( 5 * 1000 );

                System.out.println( "B : data1をロックします" );
                synchronized( data1 )
                {
                    System.out.println( "B : data1ロック開始" );

                    System.out.println( "B : data1ロック終了" );
                }
                System.out.println( "B : data1のロックを解放しました" );

                System.out.println( "B : data2ロック終了" );
            }
            System.out.println( "B : data2のロックを解放しました" );
        }
        catch( InterruptedException e )
        {
            // sleep()メソッドが途中で中断されると
            // InterruptedException例外が投げられます。
            // 滅多にないですが。
            e.printStackTrace();
        }
    }
}

/**
*   データクラス。
*/
class DataClass
{
}

この単語を含むページ

「みだし」に含まれているページ

「解説」に含まれているページ

「サンプルプログラムとか」に含まれているページ

はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
Yahoo!ブックマーク 詳細を表示 users
del.icio.us 登録する RSSに登録
サンプルを別ウィンドウで表示
サンプルをクリップボードへコピー(WindowsでIEの場合のみ)
update:2005/10/13
このページは、Javaプログラミング言語についての用語を網羅した辞書「JavaA2Z」の一ページです。
詳しくは「JavaA2Z」表紙の説明をご覧ください。