JAVAマルチスレッド(四)
デッドロック
1、デッドロックとは
簡単に言えば、デッドロックは、システム内のスレッドが互いに占有するリソースを待っているために実行を一時停止し、システムが偽死する現象である.
2、デッドロックはどのように発生したのか
システムには2つの反発リソースAとBがあると仮定し、システム内の2つのスレッド1と2はいずれもAとBを取得してから正常に動作するが、スレッド1はリソースAを先に取り、リソースBをスレッド2はリソースBを先に取り、リソースAをスレッド2は先に取る.これにより、スレッド1がリソースAを先に申請し、リソースBを申請する準備をしている間に、プロセッサスケジューリングによりスレッド2が実行を開始する場合がある.スレッド2は、リソースBを申請し、リソースAを取得する準備をしている間に、リソースAが他のスレッド(1)に占有されていることに気づき、スレッド2が実行を一時停止する.スレッド1が次に実行されると、リソースBが他のスレッド(2)によって占有されていることがわかる.このように,スレッド1と2は互いに占有するリソースを待ち続け,システムが偽死する現象をもたらす.
3、デッドロックの例
次の例では、上記のデッドロック現象を簡単にシミュレートします.
4、デッドロック発生の必要条件
デッドロックの発生には4つの必要条件を満たす必要があります.反発条件:プロセスが割り当てられたリソースを排他的に使用すること、すなわち、一定期間に1つのプロセスのみがリソースを占有することを指す.この時点で他のプロセスがリソースを要求している場合、リクエスト者は、リソースを占有するプロセスが解放されるまで待つしかありません. 要求および保持条件:プロセスが少なくとも1つのリソースを保持しているが、他のプロセスによって占有されている新しいリソース要求が提案されていることを意味し、要求プロセスはブロックされているが、自分が取得した他のリソースは保持されない. 条件を剥奪しない:プロセスが獲得した資源を指し、使用が完了する前に剥奪されず、使用が完了したときに自分で解放されるしかない. サイクル待ち条件:デッドロックが発生した場合、必然的に1つのプロセス--リソースのリングチェーンが存在し、すなわちプロセス集合{P 0,P 1,P 2,...,Pn}のP 0が1つのP 1が占有するリソースを待っていることを指す.P 1はP 2によって占有されるリソースを待っている、…、PnはP 0によって占有されたリソースを待っている. 5、デッドロックの解決
デッドロックは4つの必要条件が満たされている場合にのみ発生するため,そのうちの1つを破壊するだけでデッドロックの発生を防止できる.
上記の例では、最も破壊しやすいのが4番目のループ待ち条件であり、スレッド2もスレッド1のように先にリソースAを申請し、次にリソースBを申請すれば4番目の条件を破壊し、デッドロックの発生を防止することができる.これまでは,上記のmain()メソッドを簡単に修正するだけでよい.
1、デッドロックとは
簡単に言えば、デッドロックは、システム内のスレッドが互いに占有するリソースを待っているために実行を一時停止し、システムが偽死する現象である.
2、デッドロックはどのように発生したのか
システムには2つの反発リソースAとBがあると仮定し、システム内の2つのスレッド1と2はいずれもAとBを取得してから正常に動作するが、スレッド1はリソースAを先に取り、リソースBをスレッド2はリソースBを先に取り、リソースAをスレッド2は先に取る.これにより、スレッド1がリソースAを先に申請し、リソースBを申請する準備をしている間に、プロセッサスケジューリングによりスレッド2が実行を開始する場合がある.スレッド2は、リソースBを申請し、リソースAを取得する準備をしている間に、リソースAが他のスレッド(1)に占有されていることに気づき、スレッド2が実行を一時停止する.スレッド1が次に実行されると、リソースBが他のスレッド(2)によって占有されていることがわかる.このように,スレッド1と2は互いに占有するリソースを待ち続け,システムが偽死する現象をもたらす.
3、デッドロックの例
次の例では、上記のデッドロック現象を簡単にシミュレートします.
public class Resource
{
private boolean isTaken = false;
public synchronized void take() throws InterruptedException
{
while (isTaken)
wait();
isTaken = true;
}
public synchronized void drop()
{
isTaken = false;
notifyAll();
}
public static void main(String[] args) throws IOException
{
Resource A = new Resource();
Resource B = new Resource();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new MyThread(A, B));
exec.execute(new MyThread(B, A));
System.in.read();
exec.shutdownNow();
}
}
class MyThread implements Runnable
{
private static int counter = 0;
private final int id = counter++;
private Resource first, second;
public MyThread(Resource first, Resource second)
{
this.first = first;
this.second = second;
}
@Override
public void run()
{
try
{
while (!Thread.interrupted())
{
first.take();
Thread.yield();
second.take();
System.out.println("Thread " + id + " working...");
TimeUnit.SECONDS.sleep(1);
first.drop();
second.drop();
Thread.yield();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
4、デッドロック発生の必要条件
デッドロックの発生には4つの必要条件を満たす必要があります.
デッドロックは4つの必要条件が満たされている場合にのみ発生するため,そのうちの1つを破壊するだけでデッドロックの発生を防止できる.
上記の例では、最も破壊しやすいのが4番目のループ待ち条件であり、スレッド2もスレッド1のように先にリソースAを申請し、次にリソースBを申請すれば4番目の条件を破壊し、デッドロックの発生を防止することができる.これまでは,上記のmain()メソッドを簡単に修正するだけでよい.
public static void main(String[] args) throws IOException
{
Resource A = new Resource();
Resource B = new Resource();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new MyThread(A, B));
exec.execute(new MyThread(A, B));
System.in.read();
exec.shutdownNow();
}