有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));
}
@Override
public 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));
}
@Override
public 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 forks
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));
}
@Override
public 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;
}
@Override
public 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++;
}
@Override
public 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 forks
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));
}
@Override
public void run() {
do {
try {
Integer i = Integer.parseInt(super.getName());
if (i % 2 == 1) {
// Odd-numbered philosopher
// Try to acquire the left fork
mutex[i].acquire();
System.out.println("哲学家" + i + "号拿起左筷子");
// Try to acquire the right fork
mutex[(i + 1) % 5].acquire();
System.out.println("哲学家" + i + "号拿起右筷子");
} else {
// Even-numbered philosopher
// Try to acquire the right fork
mutex[(i + 1) % 5].acquire();
System.out.println("哲学家" + i + "号拿起右筷子");
// Try to acquire the left fork
mutex[i].acquire();
System.out.println("哲学家" + i + "号拿起左筷子");
}
// Eat
System.out.println("哲学家" + i + "号吃饭!");
// Release the forks
mutex[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 forks
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));
}
@Override
public 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 fork
mutex[i].acquire();
System.out.println("哲学家" + i + "号拿起左筷子");
// Try to acquire the right fork
mutex[(i + 1) % 5].acquire();
System.out.println("哲学家" + i + "号拿起右筷子");
} else {
// Even-numbered philosopher
// Try to acquire the right fork
mutex[(i + 1) % 5].acquire();
System.out.println("哲学家" + i + "号拿起右筷子");
// Try to acquire the left fork
mutex[i].acquire();
System.out.println("哲学家" + i + "号拿起左筷子");
}
// Eat
System.out.println("哲学家" + i + "号吃饭!");
// Release the forks
mutex[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();
}
}
}
}