食事哲学者によるJavaのセマフォの問題
-
03-07-2019 - |
質問
食事哲学の問題におけるセマフォの基本的な要点を学ぼうとしています。現在、クラス箸の配列があり、各箸には1つの使用可能な許可を持つセマフォがあります。
public class Chopstick
{
Thread holder = null;
private Semaphore lock = new Semaphore(1);
public synchronized void take() throws InterruptedException
{
this.lock.acquire();
holder = Thread.currentThread();
}
public synchronized void release()
{
this.lock.release();
holder = null;
}
}
ホルダー変数は、必要かどうかわからない関数に使用されます。
public synchronized void conditionalRelease()
{
if (holder == Thread.currentThread())
{
holder = null;
this.lock.release();
}
}
プログラムはコンパイルおよび実行されますが、箸のリリースに問題があるようです。時々、箸は解放されますが、解放されないこともあります。彼らが解放しない場合、すべての箸が取られて一人の哲学者が空腹になると、プログラムは最終的にハングアップします。
ランダムな時間の後に箸をリリースするためのPhilosopherクラス内のコードは次のとおりです。
System.out.println(this.name + " is eating");
Thread.sleep(this.getRandTime());
System.out.println(this.name + " has finished eating");
rightChopstick.release();
System.out.println(this.name + " has released the right chopstick");
leftChopstick.release();
System.out.println(this.name + " has released the left chopstick");
たとえば、私のプログラムは「哲学者0が食べ終わった」を出力し、実行を継続します。他の2行は出力されないため、明らかに私がリリースしている方法に問題があります。
ご協力いただければ幸いです。
解決
メソッドシグネチャから「同期」キーワードを削除します。外部ロックメカニズム(この場合はセマフォ)を使用しています。 「synchronized」キーワードは、オブジェクト自体のミューテックスを使用してロックを取得しようとしています。デッドロックを引き起こしていると思われる2つのリソースをロックしています。
他のヒント
問題は、thread1に特定の箸があり、別の箸が同じ箸を取得しようとすると、 this.lock.acquire()行の
が、オブジェクト自体のモニターを解放しない。 take()
-メソッドで待機することです。 ;
現在、thread1が箸を解放しようとすると、 take()
で待機している他のスレッドによってまだロックされているため、 release()
メソッドを入力できません。それはデッドロックです
箸をロックし、サイズ1のセマフォを保持していることは少し混乱しているように見えます。一般に、セマフォはリソースにチケットを提供します。チケットが1つしかない場合、それは実質的に相互排除です。ロック(同期ブロックまたはLockオブジェクトのいずれか)。実際に箸をロックオブジェクトにすることを検討してください。
もし興味があればJavaで食事をする哲学者についてのブログ投稿をしましたが、他の戦略を使ってデッドロックを回避する方法については本当にです。
ロックまたは同期キーワードが使用されていないことを確認してください。以下のチョップスティックのコードは私にとっては問題なく機能します。
public class Chopstick {
private boolean inuse;
Semaphore sem;
public Chopstick(){
inuse = false;
sem = new Semaphore(1);
}
public void pickUp()
{
try
{
while(inuse)
{
try
{
sem.acquire();
}
catch(InterruptedException e) {}
}
inuse = true;
}catch(Exception e){}
}
public void putDown()
{
try
{
inuse = false;
sem.release();
}
catch (Exception e){}
}
}
Philospherは、食事を開始する前に両方の箸のロックを取得する必要があります。最初に左をピックアップし、右を待ってから食事を開始するため、開始方法を同期する必要があります。 以下の方法で機能します:
public synchronized void startEating() {
leftChopstick.acquire();
rightChopstick.acquire();
}
public void finishEating(int id) {
leftChopstick.release();
rightChopstick.release();
}