Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

Java

デッドロックとライブロックとは?両者の違いやサンプルプログラムをご紹介

投稿日:2021年6月19日 更新日:

 

<目次>

(1) デッドロックとライブロックとは?両者の違いやサンプルプログラムをご紹介
 (1-1) デッドロック
  (1-1-1) 概要
  (1-1-2) サンプルプログラム
 (1-2) ライブロック
  (1-2-1) 概要
  (1-2-2) サンプルプログラム

(1) デッドロックとライブロックとは?両者の違いやサンプルプログラムをご紹介

(1-1) デッドロック

(1-1-1) 概要

デッドロックは複数のプロセスがお互いのリソースのロック解放を待っており、お互いが前に進めなくなっている状態を言います。
 
例えば次の図のように「プロセス①」が「リソース②」を使いつつ「リソース①」の解放を待っている状態で、逆に「プロセス②」が「リソース①」を使いつつ「リソース②」の解放を待っている状態とします。このような状態ではお互いがお互いの解放を待っているので、デッドロックが発生します。
 
(図111)イメージ図

 

デッドロックは多数のプロセスが同一のリソースを共有するような状況下で発生しうる問題になります。
 

(1-1-2) サンプルプログラム

●デッドロックを引きおこすクラス
スレッド「t1」は「someFunction1」を実行し、strAを占有してstrBが解放されたら、それらを合体させようとします。一方で、スレッド「t2」は「someFunction2」を実行し、strBを占有してstrAが解放されたら、それらを合体させようとします。この状況下だと、お互いがお互いを待って永遠に進まない「デッドロック」が発生します。
 
public class DeadLockSampleClass {

  private static String strA = "abcde";
  private static String strB = "fghij";

  public void someFunction1() {
    synchronized(strA) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("Thread#1 : strB の開放待ち");
      synchronized(strB) {
        String strC = strA + strB;
        System.out.println("Thread#1 : 完成品 = "+strC);
      }
    }
  }

  public void someFunction2() {
    synchronized(strB) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("Thread#2 : strA の開放待ち");
      synchronized(strA) {
        String strD = strA + strB;
        System.out.println("Thread#2 : 完成品 = "+strD);
      }
    }
  }

}
 
(補足)
synchronizedについては下記の記事を参照頂けたらと思います。
 
●上記を実行するmain文
 
public class DeadLockSample {

  static final DeadLockSampleClass dlock = new DeadLockSampleClass();
  public static void main(String args[]) {
    Thread t1 = new Thread(new Runnable() {
      public void run() {
        dlock.someFunction1();;
      }
    });
    t1.start();
    Thread t2 = new Thread(new Runnable() {
      public void run() {
        dlock.someFunction2();;
      }
    });
    t2.start();
  }
}

 

(図112)実行結果

 

上記プログラムを実行して、デッドロックが起きる様子を動画にしています。
 
(操作動画)

目次にもどる

(1-2) ライブロック

(1-2-1) 概要

ライブロックもデッドロックと非常に似ていますが、相違点としてライブロックに関係するプロセスは、相手の状態によって、自身の状態も定期的に変化していき、その結果としてお互いに進展しない状態を表しています。

有名?な現実世界の例えとして、二人の人が細い道ですれ違う際に、お互いに同じ方向に譲り合いを繰り返し、ぶつかりそうになりながら進めない状態ってありますよね?その状態がライブロックに似ています。

(図121)イメージ図
 

目次にもどる

(1-2-2) サンプルプログラム

●道を通るために相手(Bさん)に道を譲る「Aさん」
public class LiveLockPerson1 {
  //# 最初は「右」に避ける
  private boolean move_right = true; //# true=右側に避けた、false=左側に避けた
  //# 相手に道を譲るメソッド
  public void moveToPass(LiveLockPerson2 lp2) {
    while(this.move_right==lp2.hasMovedRight()) {
      try {
        Thread.sleep(1000);
        //# では、私が反対方向に避けますので、どうぞお通りくだだい
        System.out.println("「A」です。私が反対方向に避けますので、どうぞお通りください");
        this.move_right = moveOpposite(this.move_right);
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("「A」です。無事に通れました。");
  }
  //# どちらの方向に避けたかどうか?を教えるメソッド
  public boolean hasMovedRight() {
    //# true=右側に避けた、false=左側に避けた
    return this.move_right;
  }
  //# 反対方向に避けるメソッド
  public boolean moveOpposite(boolean isRight) {
    //# いま右なら左に避ける
    if(isRight==true) {
      return false;
    }
    //# いま左なら右に避ける
    else {
      return true;
    }
  }
}

●道を通るために相手(Aさん)に道を譲る「Bさん」
public class LiveLockPerson2 {
  //# 最初は「右」に避ける
  private boolean move_right = true; //# true=右側に避けた、false=左側に避けた
  //# 相手に道を譲るメソッド
  public void moveToPass(LiveLockPerson1 lp1) {
    while(this.move_right==lp1.hasMovedRight()) {
      try {
        Thread.sleep(1000);
        //# では、私が反対方向に避けますので、どうぞお通りくだだい
        System.out.println("「B」です。私が反対方向に避けますので、どうぞお通りください");
        this.move_right = moveOpposite(this.move_right);
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("「B」です。無事に通れました。");
  }
  //# どちらの方向に避けたかどうか?を教えるメソッド
  public boolean hasMovedRight() {
    //# true=右側に避けた、false=左側に避けた
    return this.move_right;
  }
  //# 反対方向に避けるメソッド
  public boolean moveOpposite(boolean isRight) {
    //# いま右なら左に避ける
    if(isRight==true) {
      return false;
    }
    //# いま左なら右に避ける
    else {
      return true;
    }
  }
}

●両者のスレッドを起動するmain文
public class LiveLockSample {

    static final LiveLockPerson1 lp1 = new LiveLockPerson1();
  static final LiveLockPerson2 lp2 = new LiveLockPerson2();
  public static void main(String args[]) {
    Thread t1 = new Thread(new Runnable() {
      public void run() {
        lp1.moveToPass(lp2);
      }
    });
    t1.start();
    Thread t2 = new Thread(new Runnable() {
      public void run() {
        lp2.moveToPass(lp1);
      }
    });
    t2.start();
  }
}
(図122)実行結果:「無限ループ」
お互いが道を譲りあい続け、結果的に無限ループになります。
 
上記プログラムを実行して無限ループする様子を以下の動画でご紹介しています。
(操作動画)
 

Adsense審査用広告コード


Adsense審査用広告コード


-Java

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

GitHubのWebAPIをコールしてユーザー情報を取得するサンプルプログラムの解説+エラー対応も2例紹介

(0)目次&概説 (1) 目的  (1-1) 記事の目的 (2) APIの概要  (2-1) APIとは?  (2-2) Web APIとは?  (2-3) Web APIの様々な呼び方   (2-3 …

Javaのフォーム認証でオリジナルの(元の)リクエスト情報を取得する方法

<目次> (1) Javaのフォーム認証でオリジナルの(元の)リクエスト情報を取得する方法  (1-1) 構文  (1-2) サンプルプログラム   ●JSP(要求ページ)   ●JSP(ログインペー …

EclipseでJavaプロジェクトを別サーバにエクスポートする方法

(0)目次&概説 (1) プロジェクトのエクスポートの手順  (1-1) プロジェクトのエクスポート@転送元サーバ  (1-2) インポート先サーバへ転送  (1-3) プロジェクトのインポート@転送 …

JSPにおけるincludeディレクティブとjsp:includeアクションの違い

<目次> (1) JSPにおけるincludeディレクティブとjsp:includeアクションの違い  (1-1) includeディレクティブ  (1-2) jsp:includeアクション  (1 …

Twitter APIのRate Limit Exceedエラー(code – 88)の意味について

  <目次> (1) Twitter APIのRate Limit Exceedエラー(code – 88)の意味について  (1-1) APIコールのリミット(Rate Limit)につい …

  • English (United States)
  • 日本語
Top