再発
箸
public class Chopsticks {
}
哲学者
public class Philosopher {
public Philosopher(String name, Chopsticks left, Chopsticks right) {
this.name = name;
this.left = left;
this.right = right;
}
private String name;
// 左手筷子
private Chopsticks left;
// 右手筷子
private Chopsticks right;
public void eating(){
synchronized (left) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (right) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.name+" is eating ...");
}
}
}
}
メイン機能
public class Main {
public static void main(String[] args) {
Chopsticks c1 = new Chopsticks();
Chopsticks c2 = new Chopsticks();
Chopsticks c3 = new Chopsticks();
Chopsticks c4 = new Chopsticks();
Chopsticks c5 = new Chopsticks();
// 分配筷子
Philosopher p1 = new Philosopher("p1",c1,c2);
Philosopher p2 = new Philosopher("p2",c2,c3);
Philosopher p3 = new Philosopher("p3",c3,c4);
Philosopher p4 = new Philosopher("p4",c4,c5);
Philosopher p5 = new Philosopher("p5",c5,c1);
new Thread(()->{
p1.eating();
}).start();
new Thread(()->{
p2.eating();
}).start();
new Thread(()->{
p3.eating();
}).start();
new Thread(()->{
p4.eating();
}).start();
new Thread(()->{
p5.eating();
}).start();
}
}
を使用する場合、最初に左手が割り当てられ、次に右手が割り当てられるため、デッドロックの問題が発生します。
jpsで表示
-
最初にプロセス番号を取得します
-
プロセス番号に従ってデッドロックのインストールステータスを表示する
-
デッドロックの詳細
解決する
上記では、哲学者p1が最初に左側の箸c1を取得し、哲学者p5が最初に左側の箸c5を取得し、次に哲学者p1が右側の箸c5を取得しますが、この時点でc5は哲学者によって占有されています。 p5であり、奪うことはできないので、p1は待機しています。他の哲学者も同様であり、デッドロックを引き起こしました。
優先度によるリソースの割り当て
上記の例では、割り当てる場合、最初に左手で配布し、次に右手で配布します。割り当て方法を変更し、リソースを順番に割り当てることができます。上記の例では、p5リソースのみが順番に割り当てられていないため、次の変更を加えることができます。
操作結果を下図に示し、最後にp5を実行します。デッドロックの問題は、リソースの順次割り当てを使用するだけで解決できますが、p5は常に最後にリソースを取得するため、枯渇の問題が発生します。
リリースを待っています
上記の例では、順次割り当てによりデッドロックループ待機状態の発生を回避しています。この例では、不可侵の状態を回避し、デッドロックを回避することができます。哲学者が左手のリソースを取得したときに、右手のリソースを時間内に申請しなかった場合、彼は占有していたすべてのリソースを解放します。
箸
箸のコードを変更して、reentrantlockを継承し、ロックして解放できるようにします。
public class Chopsticks extends ReentrantLock {
}
哲学者
食べる方法のコードを変更しても、他のコードは変更されません。哲学者が左手を手に入れることができない場合、それは待ち続けます。哲学者が左手の箸を手に入れ、右手の箸を手に入れなかった場合、彼は左手の箸を放します。
public void eating(){
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (left.tryLock()) {
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(right.tryLock()) {
try{
System.out.println(this.name+" is eating ...");
break;
}finally {
right.unlock();
}
}
} finally {
left.unlock();
}
}
}
}
操作結果は以下のとおりです。p5は常に最後にあるとは限らないので、空腹の問題はありません。