java多线程--哲学家就餐问题
2015-11-26 14:53
525 查看
在使用java中的sychronized或者显示锁来进行互斥操作时,就可能会出现死锁的情况:即任务一获得A资源,等待B资源。任务二获得B资源等待C资源。任务三获得C资源等待D资源。而任务四获得D资源等待A资源,这样就造成一个连续的循环等待,没有哪个线程能够继续下去,称为死锁。
对于死锁,哲学家就餐问题就是一个经典的例子。
问题描述[1]:
在一个圆桌上,有n个哲学家,n只筷子,每个哲学家左右两边各返一只筷子。哲学家可以进行思考和吃饭,思考时,不获取筷子。吃饭时,必须同时获得左右两只筷子才能吃饭(先获得右边,再获得左边)。
先来看看chopstick类:
Philosopher类:
哲学家先获得右边的筷子,再获得左边的筷子。在获得左边的筷子时,若左边的筷子已经被相邻哲学家获得,则需要等待直到其释放该资源为止,即left.take()会阻塞,直到被通知。
产生死锁的版本:
测试类中有5个哲学家和5只筷子,将思考时间定为0,就会很快出现死锁的情况。
运行结果:
分析运行结果会发现,
Philosopher [index=0]获得right stick[0],请求left stick
Philosopher [index=1]获得right stick[1],请求left stick
Philosopher [index=2]获得right stick[2],请求left stick
Philosopher [index=3]获得right stick[3],请求left stick
Philosopher [index=4]获得right stick[4],请求left stick
而没有一个能够同时获得两只筷子吃饭的哲学家。
产生死锁的条件:(简要)
互斥条件。至少有一个资源(筷子)只能同时被一个任务获得。
循环等待。这里每个哲学家都按照先获得右边的筷子,然后再获得左边的筷子的方式进行,那么就会构成一个循环等待。
修改后的测试类:
破坏第二个产生死锁的条件:循环等待。
将第五个哲学家就餐拿起筷子的顺序更改,不使之构成循环条件。
[1].thinking in java
对于死锁,哲学家就餐问题就是一个经典的例子。
问题描述[1]:
在一个圆桌上,有n个哲学家,n只筷子,每个哲学家左右两边各返一只筷子。哲学家可以进行思考和吃饭,思考时,不获取筷子。吃饭时,必须同时获得左右两只筷子才能吃饭(先获得右边,再获得左边)。
先来看看chopstick类:
package philosopher; /** * 满足: * 每根筷子同时只能被一个哲学家获得,若有另外一个哲学家请求获得该筷子,则需要等待 * 哲学家使用完筷子之后就放回并通知其他哲学家使用 * @author lecky * */ public class Chopstick { private int index; private boolean use = false; public Chopstick(int index) { super(); this.index = index; } @Override public String toString() { return "Chopstick [index=" + index + "]"; } /* * 获得筷子 * 该筷子被获得之后,当有其他哲学家线程来请求获得时,都需要等待 */ public synchronized void take() throws InterruptedException{ while(use) wait(); use =true; } /* * 归还筷子 * 当持有该筷子的哲学家使用完毕之后,就将其归还,并通知其他在等待该筷子资源的哲学家 */ public synchronized void drop(){ use = false; notifyAll(); } }
Philosopher类:
哲学家先获得右边的筷子,再获得左边的筷子。在获得左边的筷子时,若左边的筷子已经被相邻哲学家获得,则需要等待直到其释放该资源为止,即left.take()会阻塞,直到被通知。
package philosopher; import java.util.Random; /** * 每个哲学家可以进行思考或者吃饭,吃饭时需要先后获得右边和左边的筷子 * 若没有同时获得右边和左边的筷子,则等待, * 若使用完之后就返回。 * @author lecky * */ public class Philosopher implements Runnable{ private Chopstick right ; private Chopstick left; private int index; private int thinkTime; public Philosopher(Chopstick right, Chopstick left, int index, int thinkingTime) { super(); this.right = right; this.left = left; this.index = index; this.thinkTime = thinkingTime; } @Override public void run() { try { while (!Thread.interrupted()) { System.out.println(this + " thinking ......."); thinking(); System.out.println(this+" start to eat and take right stick"); right.take(); System.out.println(this+" take left stick"); left.take(); System.out.println(this+" eating"); thinking();//吃饭 right.drop(); left.drop(); } } catch (InterruptedException e) { System.out.println(this+"InterruptedException"); } } /** * 哲学家思考时间,由thinkingTime因子决定 * @throws InterruptedException */ private void thinking() throws InterruptedException{ Thread.sleep(thinkTime*100); } @Override public String toString() { return "Philosopher [index=" + index + "]"; } }
产生死锁的版本:
测试类中有5个哲学家和5只筷子,将思考时间定为0,就会很快出现死锁的情况。
package philosopher; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; /** * 会产生死锁的版本 * 5个哲学家,5只筷子,每个哲学家吃饭之前需要先拿到右边的筷子,然后再拿到左边的筷子 * 之后才能吃饭 * @author lecky * */ public class DeadlockPhilosopher { @Test public void test() throws InterruptedException { ExecutorService executor = Executors.newCachedThreadPool(); int size=5; int thinkingTime=0; Chopstick[] chopstick = new Chopstick[size]; for(int i=0;i<size;i++) chopstick[i] = new Chopstick(i); for(int i=0;i<size;i++) executor.execute(new Philosopher(chopstick[i], chopstick[(i+1)%size], i, thinkingTime)); Thread.sleep(4*1000); executor.shutdownNow(); } }
运行结果:
分析运行结果会发现,
Philosopher [index=0]获得right stick[0],请求left stick
Philosopher [index=1]获得right stick[1],请求left stick
Philosopher [index=2]获得right stick[2],请求left stick
Philosopher [index=3]获得right stick[3],请求left stick
Philosopher [index=4]获得right stick[4],请求left stick
而没有一个能够同时获得两只筷子吃饭的哲学家。
Philosopher [index=0] thinking ....... Philosopher [index=3] thinking ....... Philosopher [index=4] thinking ....... Philosopher [index=1] thinking ....... Philosopher [index=4] start to eat and take right stick Philosopher [index=3] start to eat and take right stick Philosopher [index=2] thinking ....... Philosopher [index=0] start to eat and take right stick Philosopher [index=2] start to eat and take right stick Philosopher [index=3] take left stick Philosopher [index=4] take left stick Philosopher [index=1] start to eat and take right stick Philosopher [index=2] take left stick Philosopher [index=0] take left stick Philosopher [index=1] take left stick Philosopher [index=1]InterruptedException Philosopher [index=0]InterruptedException Philosopher [index=2]InterruptedException Philosopher [index=3]InterruptedException Philosopher [index=4]InterruptedException
产生死锁的条件:(简要)
互斥条件。至少有一个资源(筷子)只能同时被一个任务获得。
循环等待。这里每个哲学家都按照先获得右边的筷子,然后再获得左边的筷子的方式进行,那么就会构成一个循环等待。
修改后的测试类:
破坏第二个产生死锁的条件:循环等待。
将第五个哲学家就餐拿起筷子的顺序更改,不使之构成循环条件。
package philosopher; import static org.junit.Assert.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; /** * 破坏产生死锁的循环条件 * 使第五个哲学家不按照先获得右边筷子,再获得左边筷子的方式进行 * @author lecky * */ public class FixedPhilosopher { @Test public void test() throws InterruptedException { ExecutorService executor = Executors.newCachedThreadPool(); int size=5; int thinkingTime = 0; Chopstick[] chopsticks = new Chopstick[size]; for(int i=0;i<size;i++) chopsticks[i]=new Chopstick(i); for(int i=0;i<size-1;i++) executor.execute(new Philosopher(chopsticks[i], chopsticks[i+1], i, thinkingTime)); executor.execute(new Philosopher(chopsticks[0], chopsticks[size-1], size, thinkingTime));//更改第五个哲学家获得筷子的顺序 Thread.sleep(100*1000); executor.shutdownNow(); } }
[1].thinking in java
相关文章推荐
- java file to inputstream以及New Document From InputStream读取和写入文件操作实例代码
- spring的aop
- 【leetcode】【46】Permutations
- JAVA基础之基本规则
- 【leetcode】【77】Combinations
- strace 调试java代码使用介绍
- Thinking in Java 整理笔记:类型信息
- Java的Struts框架简介与环境配置教程
- Spring 引入properties配置文件的方式
- Java 开源网址
- 解决Eclipse semantic error
- Java Eclipse部分图标汇总
- Spring Mock--用于Spring 的单元测试
- 将Java Project打包成jar,并生成exe
- 【leetcode】【74】Search a 2D Matrix
- Java实现的并发任务处理实例
- java.net.SocketImpl
- Java封装例题
- 【leetcode】【162】Find Peak Element
- 黑马程序员---Java基础篇之JDK的安装和介绍