javaマルチスレッド学習のデッドロックのシミュレーションと回避(実例解説)


1.デッドロック
デッドロックは、複数のスレッドが同時にブロックされ、それらのうちの一つまたは全部が、あるリソースのリリースを待っている場合である。スレッドは無期限にブロックされているため、プログラムが正常に終了することは不可能です。
Javaのデッドロックによって生成される4つの必要条件:
1、互いに反発して使って、つまり資源が1つのスレッドに使われた時、他のスレッドは使えません。
2、略奪してはいけません。資源要求者は資源占有者から強制的に資源を奪取することができません。資源は資源占有者から自発的に解放されます。
3、要求と保持、つまり資源要求者が他の資源を要求しながら、元の資源に対する占有を維持する。
4、循環待ち、すなわち待ち行列が存在する。P 1はP 2の資源を占め、P 2はP 3の資源を占め、P 3はP 1の資源を占有する。このようにしてループを待つことができます。
上記の四つの条件が成立すると、デッドロックが形成されます。もちろん、デッドロックの場合は上記のいずれかの条件を打破すれば、デッドロックは消えることができます。
2.デッドロックをシミュレートする

package com.tl.skyLine.thread; 
 
import java.util.Date; 
 
/** 
 * Created by tl on 17/3/3. 
 */ 
public class DeadLock { 
 public static String bowl = " "; 
 public static String chopsticks = "  "; 
 
 public static void main(String[] args) { 
  LockA la = new LockA(); 
  new Thread(la).start(); 
  LockB lb = new LockB(); 
  new Thread(lb).start(); 
 } 
 
} 
 
 
class LockA implements Runnable { 
 public void run() { 
  try { 
   System.out.println(new Date().toString() + "          "); 
   while (true) { 
    synchronized (DeadLock.bowl) { 
     System.out.println(new Date().toString() + "       "); 
     Thread.sleep(3000); //       B      
     synchronized (DeadLock.chopsticks) { 
      System.out.println(new Date().toString() + "        "); 
      Thread.sleep(60 * 1000); //    ,       
     } 
    } 
   } 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
} 
 
class LockB implements Runnable { 
 public void run() { 
  try { 
   System.out.println(new Date().toString() + "          "); 
   while (true) { 
    synchronized (DeadLock.chopsticks) { 
     System.out.println(new Date().toString() + "        "); 
     Thread.sleep(3000); //       A      
     synchronized (DeadLock.bowl) { 
      System.out.println(new Date().toString() + "       "); 
      Thread.sleep(60 * 1000); //    ,       
     } 
    } 
   } 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
} 
結果:

Fri Mar 03 16:34:36 CST 2017           
Fri Mar 03 16:34:37 CST 2017         
Fri Mar 03 16:34:36 CST 2017           
Fri Mar 03 16:34:37 CST 2017        
その結果、陳頂天さんはお箸を持ってきました。置いてはいけません。鄒保健さんはお椀を奪っても、どうしても手放さないです。でも、お箸は一つと茶碗一つしかありません。
3.デッドロックを避ける
もし私たちが陳頂天と鄒保健同窓生の同僚だったら、彼らが餓死しているのを見るに忍びないと思いますが、どうすればいいですか?
考えが起こらないように工夫します。ここではロック順序(スレッドは一定の順序でロックします)を紹介します。もう一つはロック時間制限(スレッドがロックを取得しようとした時に一定の時間制限を加え、期限を過ぎるとロックに対する要求を放棄し、自分の占有するロックを解除する)です。
3.1ロック順
複数のスレッドは同じロックが必要ですが、順番にロックをかけるとデッドロックが発生しやすくなります。すべてのスレッドが同じ順番でロックされていることを確認できれば、デッドロックは発生しません。私達の上のコードはデッドロックをシミュレートするために、スレッドLockAとLockBの同僚の資源獲得の順序を調整しました。LockAは先にお椀を奪い、その後sleep 3秒、LockBは先にお箸を奪い取って、私達は今資源の順序を変えます。二つのスレッドは先にお椀を奪い取って、お箸を奪い取って、厳格にこの順番に運行します。お椀を奪うことから、お椀が取れない状況でお箸を奪ってはいけません。そうすると、デッドロックが発生しないようにします。これもデッドロックを避ける一番簡単な方法です。
コードの修正は以下の通りです。

/** 
 * Created by tl on 17/3/3. 
 */ 
public class UnDeadLock { 
 public static String bowl = " "; 
 public static String chopsticks = "  "; 
 
 public static void main(String[] args) { 
  LockA la = new LockA(); 
  new Thread(la).start(); 
  LockB lb = new LockB(); 
  new Thread(lb).start(); 
 } 
 
} 
 
 
class LockA implements Runnable { 
 public void run() { 
  try { 
   System.out.println(new Date().toString() + "          "); 
   while (true) { 
    synchronized (UnDeadLock.bowl) { 
     System.out.println(new Date().toString() + "       "); 
     synchronized (UnDeadLock.chopsticks) { 
      System.out.println(new Date().toString() + "        "); 
     } 
    } 
    Thread.sleep(5000); 
   } 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
} 
 
class LockB implements Runnable { 
 public void run() { 
  try { 
   System.out.println(new Date().toString() + "          "); 
   while (true) { 
    synchronized (UnDeadLock.bowl) { 
     System.out.println(new Date().toString() + "       "); 
     synchronized (UnDeadLock.chopsticks) { 
      System.out.println(new Date().toString() + "        "); 
     } 
    } 
    Thread.sleep(5000); 
   } 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
} 
実行結果

Fri Mar 24 11:16:51 CST 2017           
Fri Mar 24 11:16:51 CST 2017           
Fri Mar 24 11:16:51 CST 2017        
Fri Mar 24 11:16:51 CST 2017         
Fri Mar 24 11:16:51 CST 2017        
Fri Mar 24 11:16:51 CST 2017         
Fri Mar 24 11:16:56 CST 2017        
Fri Mar 24 11:16:56 CST 2017         
Fri Mar 24 11:16:56 CST 2017        
Fri Mar 24 11:16:56 CST 2017         
Fri Mar 24 11:17:01 CST 2017        
Fri Mar 24 11:17:01 CST 2017         
もうロックがかかりません。
3.2ロック時効をかける
加錠時効の原理は、各アクセススレッドにアクセス時効を増加させ、スレッドが所定の時間内に必要なすべてのロックを成功裏に獲得しないと、取得したすべてのロックをキャンセルして解除します。
この目標を達成するために、私たちは表示されたロックを使わずに、信号量Semaphoreで制御します。信号量は資源を制御できます。ここでは一つのスレッドにしかアクセスできないように指定します。信号量は取得するタイムアウト時間を指定できます。このタイムアウト時間に合わせて追加処理ができます。取得できない場合は、試行を繰り返したり、試行の回数を指定したりしてすぐに終了します。

package com.tl.skyLine.thread; 
 
import java.util.Date; 
import java.util.concurrent.Semaphore; 
import java.util.concurrent.TimeUnit; 
 
/** 
 * Created by tl on 17/3/3. 
 */ 
public class UnDeadLock { 
 public static String bowl = " "; 
 //                   
 public static final Semaphore a1 = new Semaphore(1); 
 public static String chopsticks = "  "; 
 //                    
 public static final Semaphore a2 = new Semaphore(1); 
 
 public static void main(String[] args) { 
  LockAa la = new LockAa(); 
  new Thread(la).start(); 
  LockBa lb = new LockBa(); 
  new Thread(lb).start(); 
 } 
 
} 
 
 
class LockAa implements Runnable { 
 public void run() { 
  try { 
   System.out.println(new Date().toString() + "          "); 
   while (true) { 
    if (UnDeadLock.a1.tryAcquire(1, TimeUnit.SECONDS)) { 
     System.out.println(new Date().toString() + "       "); 
     if (UnDeadLock.a2.tryAcquire(1, TimeUnit.SECONDS)) { 
      System.out.println(new Date().toString() + "        ,     ,    "); 
      Thread.sleep(60 * 1000 * 10); //          ,        
     } else { 
      System.out.println(new Date().toString() + "        ,        "); 
     } 
    } else { 
     System.out.println(new Date().toString() + "       ,       "); 
    } 
 
    UnDeadLock.a1.release(); //    
    UnDeadLock.a2.release(); 
    System.out.println(new Date().toString() + "                "); 
    Thread.sleep(1000); //       ,     do something      
   } 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
} 
 
 
class LockBa implements Runnable { 
 public void run() { 
  try { 
   System.out.println(new Date().toString() + "          "); 
   while (true) { 
    if (UnDeadLock.a2.tryAcquire(1, TimeUnit.SECONDS)) { 
     System.out.println(new Date().toString() + "        "); 
     if (UnDeadLock.a1.tryAcquire(1, TimeUnit.SECONDS)) { 
      System.out.println(new Date().toString() + "       ,     ,    "); 
      Thread.sleep(60 * 1000 * 10); //          ,        
     } else { 
      System.out.println(new Date().toString() + "       ,       "); 
     } 
    } else { 
     System.out.println(new Date().toString() + "        ,        "); 
    } 
 
    UnDeadLock.a1.release(); //    
    UnDeadLock.a2.release(); 
    System.out.println(new Date().toString() + "                "); 
    Thread.sleep(10 * 1000);//        ,  tryAcquire  1 ,  B  A        ,          
   } 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
} 
結果

Fri Mar 03 18:12:07 CST 2017           
Fri Mar 03 18:12:07 CST 2017           
Fri Mar 03 18:12:07 CST 2017        
Fri Mar 03 18:12:07 CST 2017         
Fri Mar 03 18:12:08 CST 2017        ,         
Fri Mar 03 18:12:08 CST 2017                 
Fri Mar 03 18:12:08 CST 2017       ,     ,     
Fri Mar 03 18:12:10 CST 2017       ,        
Fri Mar 03 18:12:10 CST 2017                 
Fri Mar 03 18:12:11 CST 2017        
Fri Mar 03 18:12:11 CST 2017        ,     ,     
明らかに、私達はデッドロックを満たす第三条を打ち破りました。つまり資源要求者が他の資源を要求しながら、元の資源の占有を維持しています。お椀とお箸を完全に奪っていない時、二人は占有の資源を全部解放して、再び資源を奪い始めました。これであなたは成功しました。あなたの同僚二人を救いました。
Semaphore ap:

acquire 
 
public void acquire() 
    throws InterruptedException 
 
            ,               ,       。      (       )     ,         1。 
 
          ,             ,                        : 
 
                release()   ,                   ;   
              。 
 
       : 
 
                 on ;   
           。 
 
     InterruptedException,              。 
 
   : 
  InterruptedException -           
 
release 
 
public void release() 
 
       ,        。      ,          1。            ,                   。            (    )   。 
 
                  acquire()      。                       。 
wait()とsleep()の違い:
sleep()メソッドはCPUのみを譲り、同期リソースのロックを解除しません!
wait()メソッドは、現在のスレッドが同期リソースを一時的にキャンセルし、他のリソースを待っているスレッドがこのリソースを取得して実行することを意味します。
以上のjavaマルチスレッド学習のデッドロックのシミュレーションと回避(実例解説)は、小編集が皆さんに共有している内容です。参考にしていただければと思います。どうぞよろしくお願いします。