題目
有N個哲學家圍坐在一張圓桌旁,桌上只有N把叉子,每對哲學家中間各有一把。
哲學家的兩種行為:
一、思考
二、吃意大利面
哲學家只能拿起手邊左邊或右邊的叉子
吃飯需要兩把叉子
正確地模仿哲學家的行為
方法一
一次只允許四個人搶叉子
import java.util.concurrent.Semaphore;
class 方法一 {public static 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 {Philosopher(int name) {super.setName(String.valueOf(name));}@Overridepublic void run() {do {try {//只有四個人有搶叉子的資格count.acquire();Integer i = Integer.parseInt(super.getName());//規定都先拿左手邊的叉子,于是四個人左手都有叉子mutex[i].acquire();//大家開始搶右邊的叉子mutex[(i + 1) % 5].acquire();//誰先搶到誰第一個開吃System.out.println("哲學家" + i + "號吃飯!");//吃完放下左手的叉子,對于左邊人來說,就是他的右叉子,直接開吃mutex[i].release();//再放下右手的叉子mutex[(i + 1) % 5].release();//吃完了,開始思考,由于放下了右手的叉子,相當于給一個叉子沒有的哲學家一個左叉子count.release();//模擬延遲Thread.sleep(2000);} catch (InterruptedException e) {System.out.println("異常");}} while (true);}}public static void main(String[] args) {Philosopher[] threads=new Philosopher[5];for (int i = 0; i < 5; i++) {threads[i] = new Philosopher(i);}for (Philosopher i : threads) {i.start();}}}
}
count每次acquire就會減一,使得第五個來訪問的哲學家被阻塞
下面是將think和eat方法分離出來的改進版本:
import java.util.concurrent.Semaphore;
public class 方法一改進 {public static 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 {Philosopher(int name) {super.setName(String.valueOf(name));}@Overridepublic void run() {do {try {think();eat();} catch (InterruptedException e) {System.out.println("異常");}} while (true);}public void think() throws InterruptedException {// 模擬思考System.out.println("哲學家" + super.getName() + "號正在思考");Thread.sleep(2000); // 模擬延遲}public void eat() throws InterruptedException {// 只有四個人有搶叉子的資格count.acquire();Integer i = Integer.parseInt(super.getName());// 規定都先拿左手邊的叉子,于是四個人左手都有叉子mutex[i].acquire();// 大家開始搶右邊的叉子mutex[(i + 1) % 5].acquire();// 誰先搶到誰第一個開吃System.out.println("哲學家" + i + "號吃飯!");// 吃完放下左手的叉子,對于左邊人來說,就是他的右叉子,直接開吃mutex[i].release();// 再放下右手的叉子mutex[(i + 1) % 5].release();// 吃完了,開始思考,由于放下了右手的叉子,相當于給一個叉子沒有的哲學家一個左叉子count.release();}}public static void main(String[] args) {PhilosopherTest.Philosopher[] threads=new PhilosopherTest.Philosopher[5];for (int i = 0; i < 5; i++) {threads[i] = new PhilosopherTest.Philosopher(i);}for (PhilosopherTest.Philosopher i : threads) {i.start();}}}}
本質沒區別。
方法二
先獲取左筷子,一段時間內申請不到右筷子就將左筷子釋放
import java.util.concurrent.Semaphore;public class 方法二 {//先獲取左筷子,一段時間內申請不到右筷子就將左筷子釋放// Five forksstatic final Semaphore[] mutex = {new Semaphore(1),new Semaphore(1),new Semaphore(1),new Semaphore(1),new Semaphore(1)};static class Philosopher extends Thread {Philosopher(int name) {super.setName(String.valueOf(name));}@Overridepublic void run() {do {try {Integer i = Integer.parseInt(super.getName());//嘗試獲取左筷子if (mutex[i].tryAcquire()) {//嘗試獲取右筷子if (mutex[(i + 1) % 5].tryAcquire()) {System.out.println("哲學家" + i + "號吃飯!");mutex[i].release();mutex[(i + 1) % 5].release();Thread.sleep(2000);} else {//如果獲取不到右筷子,就把左筷子扔了mutex[i].release();}}//這里沒有else,獲取不到左筷子就一直嘗試} catch (InterruptedException e) {System.out.println("異常");}} while (true);}}public static void main(String[] args) {Philosopher[] threads=new Philosopher[5];for (int i = 0; i < 5; i++) {threads[i] = new Philosopher(i);}for (Philosopher i : threads) {i.start();}}
}
下面是將eat和think分出來的版本:
import java.util.concurrent.Semaphore;
class 方法二改進 {//先獲取左筷子,一段時間內申請不到右筷子就將左筷子釋放public static class Philosopher extends Thread {private static Semaphore[] chopsticks = {new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1)};private int id;public Philosopher(int id) {this.id = id;}@Overridepublic void run() {while (true) {think();eat();}}public void think() {System.out.println("哲學家_" + this.id + "正在思考");//思考一秒時間try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}public void eat() {try {if (chopsticks[this.id].tryAcquire()) { // 獲取左筷子if (chopsticks[(this.id + 1) % chopsticks.length].tryAcquire()) { // 獲取右筷子System.out.println("哲學家_" + this.id + "正在吃飯");// 吃飯花一秒時間try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {chopsticks[this.id].release(); // 放下左筷子chopsticks[(this.id + 1) % chopsticks.length].release(); // 放下右筷子}} else {chopsticks[this.id].release(); // 如果不能獲取右筷子,釋放左筷子}}} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {Philosopher[] threads=new Philosopher[5];for (int i = 0; i < 5; i++) {threads[i] = new Philosopher(i);}for (Philosopher i : threads) {i.start();}}}}
下面是用可重入鎖實現的版本:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Philosopher extends Thread {private Lock leftFork;private Lock rightFork;private int philosopherId;private int eatCount; // 計數器public Philosopher(int philosopherId, Lock leftFork, Lock rightFork) {this.philosopherId = philosopherId;this.leftFork = leftFork;this.rightFork = rightFork;this.eatCount = 0;}private void think() throws InterruptedException {System.out.println("Philosopher " + philosopherId + " is thinking.");Thread.sleep((long) (Math.random() * 1000));}private void eat() throws InterruptedException {System.out.println("Philosopher " + philosopherId + " is eating.");Thread.sleep((long) (Math.random() * 1000));eatCount++;}@Overridepublic void run() {try {while (eatCount < 1) { // 表示每個哲學家吃了1次think();/* 使用ReentrantLock鎖, 該類中有一個tryLock()方法, 在指定時間內獲取不到鎖對象, 就從阻塞隊列移除,不用一直等待。當獲取了左手邊的筷子之后, 嘗試獲取右手邊的筷子, 如果該筷子被其他哲學家占用, 獲取失敗, 此時就先把自己左手邊的筷子,給釋放掉. 這樣就避免了死鎖問題 */if (leftFork.tryLock()) {System.out.println("Philosopher " + philosopherId + " picked up left fork.");if (rightFork.tryLock()) {System.out.println("Philosopher " + philosopherId + " picked up right fork.");eat();rightFork.unlock();System.out.println("Philosopher " + philosopherId + " put down right fork.");}leftFork.unlock();System.out.println("Philosopher " + philosopherId + " put down left fork.");}}} catch (InterruptedException e) {e.printStackTrace();}}
}class DiningPhilosophers {public static void main(String[] args) {int numPhilosophers = 5;Philosopher[] philosophers = new Philosopher[numPhilosophers];Lock[] forks = new ReentrantLock[numPhilosophers];for (int i = 0; i < numPhilosophers; i++) {forks[i] = new ReentrantLock();}for (int i = 0; i < numPhilosophers; i++) {philosophers[i] = new Philosopher(i, forks[i], forks[(i + 1) % numPhilosophers]);philosophers[i].start();}// 等待所有哲學家線程結束for (Philosopher philosopher : philosophers) {try {philosopher.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("All philosophers have finished eating. Program ends.");}
}
? 使用ReentrantLock鎖, 該類中有一個tryLock()方法, 在指定時間內獲取不到鎖對象, 就從阻塞隊列移除,不用一直等待。當獲取了左手邊的筷子之后, 嘗試獲取右手邊的筷子, 如果該筷子被其他哲學家占用, 獲取失敗, 此時就先把自己左手邊的筷子給釋放掉. 這樣就避免了死鎖問題
?方法三
奇數哲學家先左后右,偶數科學家先右后左
import java.util.concurrent.Semaphore;public class 方法四 {//奇數哲學家先左后右,偶數科學家先右后左// Five forksstatic final Semaphore[] mutex = { new Semaphore(1),new Semaphore(1),new Semaphore(1),new Semaphore(1),new Semaphore(1) };static class Philosopher extends Thread {Philosopher(int name) {super.setName(String.valueOf(name));}@Overridepublic void run() {do {try {Integer i = Integer.parseInt(super.getName());if (i % 2 == 1) {// Odd-numbered philosopher// Try to acquire the left forkmutex[i].acquire();System.out.println("哲學家" + i + "號拿起左筷子");// Try to acquire the right forkmutex[(i + 1) % 5].acquire();System.out.println("哲學家" + i + "號拿起右筷子");} else {// Even-numbered philosopher// Try to acquire the right forkmutex[(i + 1) % 5].acquire();System.out.println("哲學家" + i + "號拿起右筷子");// Try to acquire the left forkmutex[i].acquire();System.out.println("哲學家" + i + "號拿起左筷子");}// EatSystem.out.println("哲學家" + i + "號吃飯!");// Release the forksmutex[i].release();mutex[(i + 1) % 5].release();// Think (sleep for simulation)Thread.sleep(2000);} catch (InterruptedException e) {System.out.println("異常");}} while (true);}}public static void main(String[] args) {Philosopher[] threads = new Philosopher[5];for (int i = 0; i < 5; i++) {threads[i] = new Philosopher(i);}for (Philosopher i : threads) {i.start();}}
}
下面是將think和eat分開的版本:
import java.util.concurrent.Semaphore;
public class 方法四改進 {public static class 方法四 {//奇數哲學家先左后右,偶數科學家先右后左// Five forksstatic final Semaphore[] mutex = { new Semaphore(1),new Semaphore(1),new Semaphore(1),new Semaphore(1),new Semaphore(1) };static class Philosopher extends Thread {Philosopher(int name) {super.setName(String.valueOf(name));}@Overridepublic void run() {do {try {think();eat();} catch (InterruptedException e) {System.out.println("異常");}} while (true);}public void think() throws InterruptedException {Integer i = Integer.parseInt(super.getName());System.out.println("哲學家" + i + "號正在思考");// Think (sleep for simulation)Thread.sleep(2000);}public void eat() throws InterruptedException {Integer i = Integer.parseInt(super.getName());if (i % 2 == 1) {// Odd-numbered philosopher// Try to acquire the left forkmutex[i].acquire();System.out.println("哲學家" + i + "號拿起左筷子");// Try to acquire the right forkmutex[(i + 1) % 5].acquire();System.out.println("哲學家" + i + "號拿起右筷子");} else {// Even-numbered philosopher// Try to acquire the right forkmutex[(i + 1) % 5].acquire();System.out.println("哲學家" + i + "號拿起右筷子");// Try to acquire the left forkmutex[i].acquire();System.out.println("哲學家" + i + "號拿起左筷子");}// EatSystem.out.println("哲學家" + i + "號吃飯!");// Release the forksmutex[i].release();mutex[(i + 1) % 5].release();}}public static void main(String[] args) {Philosopher[] threads = new Philosopher[5];for (int i = 0; i < 5; i++) {threads[i] = new Philosopher(i);}for (Philosopher i : threads) {i.start();}}}}