哲学家就餐是经典的PV操作。
一个哲学家同时拿起左边的筷子和右边的筷子进行就餐,每一个哲学家都会等待右边的筷子,具备了死锁问题之一的循环等待。
基础的哲学家就餐问题代码
在Java中,Semaphore
是一个用于控制对某个资源的访问的同步工具。它主要用于管理对有限资源的访问,确保在多线程环境中安全地控制资源的使用。
- 信号量:
Semaphore
是一个计数信号量,维护一个计数器,表示可用资源的数量。 - 获取与释放:线程可以通过
acquire()
方法获取一个许可,当资源可用时,计数器减一;如果没有可用资源,线程会被阻塞。释放资源时,使用release()
方法,计数器加一。 - acquire()就是p操作,获取资源。
- release()就是v操作,用来释放资源。
import java.util.Arrays;
import java.util.concurrent.Semaphore;public class PhilosopherMeal extends Thread {private static String name = "哲学家";private int philosopherNum; //哲学家编号public static Semaphore[] semaphores = new Semaphore[5];public PhilosopherMeal(int philosopherNum) {//调用父类的Thread创建线程super(PhilosopherMeal.name + String.valueOf(philosopherNum));this.philosopherNum = philosopherNum;}@Overridepublic void run() {try {semaphores[this.philosopherNum].acquire(); //获取锁Thread.sleep((long) (Math.random() * 1)); //拿起筷子的时间,会发生死锁//acquire(): 获取一个许可。如果信号量的许可已经被其他线程占用,那么当前线程会阻塞,直到有许可可用。semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +"和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");} catch (Exception e) {e.printStackTrace();} finally {//释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。semaphores[(this.philosopherNum)].release();semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();}}//哲学家拿起右边的筷子吃饭private int getRightSemaphoreIndex(int philosopherNum) {return (philosopherNum - 1 < 0) ? semaphores.length - 1 : philosopherNum - 1;}public static void main(String[] args) {// Arrays.fill(semaphores,new Semaphore(1));for (int i = 0; i < semaphores.length; i++) {semaphores[i] = new Semaphore(1);}for (int i = 0; i < 5; i++) {new PhilosopherMeal(i).start();}}
}
发生死锁
没有发生死锁
解决方案
1.打破死锁的条件之一 循环条件等待,我们可以破坏循环的条件从而来解决死锁。让奇数的筷子先拿左边的筷子,后拿右边的筷子。
偶数的筷子先拿右边的筷子,后那左边的筷子,这样就可以解决死锁的问题。破快了相互之间循环等待的条件。
@Override
public void run() {try {int n = this.philosopherNum; //拿到哲学家的编号if (n % 2 == 1) { //奇数的先拿左边筷子 偶数拿右边semaphores[this.philosopherNum].acquire(); //获取锁Thread.sleep((long) (1000)); //拿起筷子的时间,会发生死锁semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();} else {semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();Thread.sleep((long) (1000)); //拿起筷子的时间,会发生死锁semaphores[this.philosopherNum].acquire(); //获取锁}//acquire(): 获取一个许可。如果信号量的许可已经被其他线程占用,那么当前线程会阻塞,直到有许可可用。System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +"和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");} catch (Exception e) {e.printStackTrace();} finally {//释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。semaphores[(this.philosopherNum)].release();semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();}
}
2.最多只能让四个哲学家同时拿起筷子,这样就保证了一定有一个哲学家可以拿到筷子。这样就不会发生死锁。
private static Semaphore count = new Semaphore(4);
@Override
public void run() {try {count.acquire(); //获取countsemaphores[this.philosopherNum].acquire(); //获取锁Thread.sleep(1000);semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +"和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");} catch (Exception e) {e.printStackTrace();} finally {//释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。semaphores[(this.philosopherNum)].release();semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();count.release();}
}
3 拿起左边筷子和右边筷子不是一个原子性的操作,我们可以加一个全局的锁,让拿左边筷子和右边的筷子变为一个原子操作。
private static Semaphore muntex = new Semaphore(1);
@Override
public void run() {try {muntex.acquire();semaphores[this.philosopherNum].acquire(); //获取锁Thread.sleep((long) (1000)); //拿起筷子的时间,会发生死锁//acquire(): 获取一个许可。如果信号量的许可已经被其他线程占用,那么当前线程会阻塞,直到有许可可用。semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].acquire();System.out.println(this.getName() + "获取到了筷子" + String.valueOf(this.philosopherNum) +"和" + String.valueOf(this.getRightSemaphoreIndex(this.philosopherNum)) + "开始吃饭了");} catch (Exception e) {e.printStackTrace();} finally {//释放一个许可,意味着当前线程已不再占用资源,其他线程可以继续使用该资源。semaphores[(this.philosopherNum)].release();semaphores[this.getRightSemaphoreIndex(this.philosopherNum)].release();muntex.release();}
}