五个哲学家围坐在一个圆桌周围,每个哲学家面前都有一只碗,各碗之间分别有一根筷子,餐桌如下图。
哲学家的生活包括两种活动:即吃饭和思考。当哲学家觉得饿时,他就分两次去取他左边和右边的筷子,每次拿一根(不能强行从邻座手中抢过筷子),如果成功,他就开始吃饭,吃完后把筷子放回原处继续思考。
筷子是临界资源,一段时间内只允许一个哲学家使用。可用一个信号量表示一支筷子。由五个信号量构成信号量数组。
//伪代码
while(TRUE){
think();
P(chopstick[i]);
P(chopstick[(i+1)mod 5]);
eat();
V(chopstick[i]);
V(chopstick[(i+1)mod 5]);
}
/**
* 保证了不会有相邻的哲学家同时进餐,但不能防止五位哲学家同时拿起各自左边的筷子、又试图去拿右侧的筷子,
* 这样会引起他们都无法进餐而无限期等待下去的情况,即发生了死锁。
*/
解决上述死锁的方法之一:最多只允许4个哲学家同时进餐;保证有一人能够进餐。
通常,为了使用信号量,希望访问共享资源的线程尝试取得许可证。如果信号量的计数大于0,就表明线程取得许可证,这会导致信号量的计数减小;否则,线程会被阻塞,直到获取许可证为止。当线程不在需要访问共享资源时,释放许可证,从而增大信号量的计数。如果还有另外一个线程在等待许可证,该线程将在这一刻取得许可证。Java中的Semaphore类实现了这一机制。
import java.util.concurrent.Semaphore;
public class PhilosopherTest {
static final Semaphore count=new Semaphore(4);///初始化信号量
static final Semaphore[] mutex={new Semaphore(1),new Semaphore(1),
new Semaphore(1),new Semaphore(1),new Semaphore(1)};//控制最多允许四位哲学家同时进餐
static class Philosopher extends Thread{
public Philosopher(String name){
super.setName(name);
}
public void run(){
do{
try{
System.out.println("哲学家"+this.getName()+"正在思考");
count.acquire();
Integer integer=Integer.parseInt(this.getName());
mutex[integer].acquire();
mutex[(integer+1)%5].acquire();
System.out.println("哲学家"+this.getName()+"正在吃饭");
mutex[integer].release();
mutex[(integer+1)%5].release();
count.release();
}catch (Exception e){
e.printStackTrace();
}
}while(true);
}
}
public static void main(String[] args) {
Philosopher p1=new Philosopher("1");
Philosopher p2=new Philosopher("2");
Philosopher p3=new Philosopher("3");
Philosopher p4=new Philosopher("4");
Philosopher p5=new Philosopher("0");
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
}
}
运行结果: