根据哲学家进餐经典问题来巩固并发带来的死锁问题以及解决方式
问题简述
哲学家进餐问题是一个经典的并发编程问题,用来探讨资源竞争和死锁的情况。问题的场景是五个哲学家围坐在一张圆桌旁,每个哲学家面前有一碗饭和一只叉子。哲学家的生活由交替进行的思考和进餐两个活动组成。但是,每个哲学家只能同时使用自己左右两边的叉子来进餐。
经典问题代码
java
@Test
public void testCase() {
Fork f1 = new Fork("1");
Fork f2 = new Fork("2");
Fork f3 = new Fork("3");
Fork f4 = new Fork("4");
Fork f5 = new Fork("5");
ExecutorService threadPool = Executors.newFixedThreadPool(5, new ThreadFactory() {
private volatile int index = 0;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "t" + (index++));
}
});
threadPool.submit(new PhilosophersTask("1", f1, f5));
threadPool.submit(new PhilosophersTask("2", f2, f1));
threadPool.submit(new PhilosophersTask("3", f3, f2));
threadPool.submit(new PhilosophersTask("4", f4, f3));
threadPool.submit(new PhilosophersTask("5", f5, f4));
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10));
threadPool.shutdown();
}
private static class PhilosophersTask implements Runnable {
private String name;
private Fork leftFork;
private Fork rightFork;
public PhilosophersTask(String name, Fork left, Fork right) {
this.name = name;
this.leftFork = left;
this.rightFork = right;
}
@Override
public void run() {
while (true) {
thinking();
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10));
dining();
}
}
private void thinking() {
System.out.println(name + " is thinking.");
}
private void dining() {
synchronized (leftFork) {
System.out.println(name + " use left " + leftFork);
//这一句很关键
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10));
synchronized (rightFork) {
System.out.println(name + " use right " + leftFork);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
}
}
}
}
/**
* 叉子
*/
private static class Fork {
private String name;
public Fork(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fork " + this.name;
}
}
执行结果
每个哲学家都拿起左边的叉子,准备用右边的叉子,形成环路等待
csharp
1 is thinking.
5 is thinking.
2 is thinking.
3 is thinking.
4 is thinking.
4 use left Fork 4
3 use left Fork 3
2 use left Fork 2
5 use left Fork 5
1 use left Fork 1
解决哲学家就餐问题
使用助手破坏死锁请求不释放的条件
代码如下
java
@Test
public void testCase() {
Fork f1 = new Fork("1");
Fork f2 = new Fork("2");
Fork f3 = new Fork("3");
Fork f4 = new Fork("4");
Fork f5 = new Fork("5");
Assistant assistant = new Assistant("a1");
ExecutorService threadPool = Executors.newFixedThreadPool(5, new ThreadFactory() {
private volatile int index = 0;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "t" + (index++));
}
});
threadPool.submit(new PhilosophersTask("1", f1, f5, assistant));
threadPool.submit(new PhilosophersTask("2", f2, f1, assistant));
threadPool.submit(new PhilosophersTask("3", f3, f2, assistant));
threadPool.submit(new PhilosophersTask("4", f4, f3, assistant));
threadPool.submit(new PhilosophersTask("5", f5, f4, assistant));
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10));
threadPool.shutdown();
}
private static class PhilosophersTask implements Runnable {
private String name;
private Fork leftFork;
private Fork rightFork;
private Assistant assistant;
public PhilosophersTask(String name, Fork left, Fork right, Assistant assistant) {
this.name = name;
this.leftFork = left;
this.rightFork = right;
this.assistant = assistant;
}
@Override
public void run() {
while (true) {
thinking();
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10));
dining();
}
}
private void thinking() {
System.out.println(name + " is thinking.");
}
private void dining() {
synchronized (leftFork) {
if (assistant.num.incrementAndGet() > 4) {
System.out.println(name + " put down left " + leftFork);
assistant.num.decrementAndGet();
return;
} else {
System.out.println(name + " use left " + leftFork);
}
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10));
synchronized (rightFork) {
System.out.println(name + " use right " + rightFork);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
}
assistant.num.decrementAndGet();
}
}
}
/**
* 叉子
*/
private static class Fork {
private String name;
public Fork(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fork " + this.name;
}
}
private static class Assistant {
protected AtomicInteger num = new AtomicInteger(0);
private String name;
public Assistant(String name) {
this.name = name;
}
}
执行结果
css
1 is thinking.
3 is thinking.
2 is thinking.
5 is thinking.
4 is thinking.
5 use left Fork 5
3 use left Fork 3
2 put down left Fork 2
1 use left Fork 1
2 is thinking.
4 use left Fork 4
2 put down left Fork 2
2 is thinking.
3 use right Fork 2
2 use left Fork 2
3 is thinking.
4 use right Fork 3
4 is thinking.
3 use left Fork 3
5 use right Fork 4
4 use left Fork 4
1 use right Fork 5
5 is thinking.
1 is thinking.
5 use left Fork 5
2 use right Fork 1
2 is thinking.
3 use right Fork 2
1 put down left Fork 1
1 is thinking.
1 use left Fork 1
3 is thinking.
2 use left Fork 2
4 use right Fork 3
4 is thinking.
5 use right Fork 4
3 use left Fork 3
4 use left Fork 4
5 is thinking.
1 use right Fork 5
1 is thinking.
5 use left Fork 5
2 use right Fork 1
使用lock的有限等待
将上述使用synchronized
改为tryLock
有效时间获取时间锁资源,如果获取不到则不再获取
死锁条件
死锁是指系统中的一组进程(或线程)由于竞争有限的资源而无法继续执行的情况。产生死锁的常见条件有以下四个:
- 互斥条件(Mutual Exclusion):至少有一个资源同时只能被一个进程(或线程)占用,即当一个进程(或线程)访问该资源时,其他进程(或线程)无法同时访问。
- 请求与保持条件(Hold and Wait):一个进程(或线程)在等待获取其他进程(或线程)占用的资源时,保持已经占有的资源不释放。
- 不可剥夺条件(No Preemption):已经分配给一个进程(或线程)的资源不能被强制性地剥夺,只能由占有该资源的进程(或线程)主动释放。
- 循环等待条件(Circular Wait):系统中存在一个进程(或线程)的等待链,使得每个进程(或线程)都在等待下一个进程(或线程)所占有的资源。
哲学家进餐针对1 3 4无法更改,只能在2 请求与保持上下功夫在解决