1问题描述

如图1.1,有5个哲学家围着一个大圆桌就餐。哲学家和筷子都按逆时针顺序编号,每个哲学家的编号与他左边的筷子的编号一致。



规定如下:

(1)哲学家之间不相互交流;

(2)哲学家只做两件事:吃饭或思考;

(3)每个哲学家只能拿其邻近的左右两支筷子;

(4)哲学家不能抢占别人已经拿起的筷子;

(5)哲学家在拿到两支筷子之前,不会放下手中的筷子;

(6)哲学家只有拿到两支筷子才开始就餐,然后又放下两支筷子;

当所有哲学家都拿起了左边的筷子时,将出现他们都在等待其右边的哲学家放下左边的筷子,这样一直等待下去,就会造成所有哲学家都吃不了饭,这种状态称为“死锁”。本文要解决的问题是,如何避免死锁,使所有哲学家都能愉快地吃饭。

2算法描述

基于Android的哲学家就餐问题源代码下载

2.1主类与布局

2.1.1主类

MainActivity.java

package com.philosohper;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast; public class MainActivity extends AppCompatActivity {
private static Button clear,start_stop,m1,m2,m3,m4;//清除、开始|暂停、方法1、方法2、方法3、方法4按钮
private static ScrollView scrollView;//滚动视图
private static TextView status;//输出的哲学家状态信息文本框
public static StringBuilder str;//输出的哲学家状态信息字符串
private Philosopher_manager philosopher_manager;//哲学家管理者 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置全屏
init_view();
philosopher_manager=new Philosopher_manager();
} private void init_view(){//初始化UI界面
clear= (Button) findViewById(R.id.clear);
start_stop= (Button) findViewById(R.id.start_stop);
m1= (Button) findViewById(R.id.m1);
m2= (Button) findViewById(R.id.m2);
m3= (Button) findViewById(R.id.m3);
m4= (Button) findViewById(R.id.m4);
scrollView= (ScrollView) findViewById(R.id.scrollView);
status= (TextView) findViewById(R.id.status);
str=new StringBuilder();
clear.setOnClickListener(cl);
start_stop.setOnClickListener(cl);
m1.setOnClickListener(cl);
m2.setOnClickListener(cl);
m3.setOnClickListener(cl);
m4.setOnClickListener(cl);
} private View.OnClickListener cl =new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.clear:
clear();//清空显示
break;
case R.id.start_stop:
start_stop();//开始|暂停
break;
case R.id.m1:
Toast.makeText(MainActivity.this,"最多拿起4个",Toast.LENGTH_LONG).show();
if(Philosopher.method==1) break;
philosopher_manager.set_methord(1);//设置防死锁方法1
break;
case R.id.m2:
Toast.makeText(MainActivity.this,"一次拿2个",Toast.LENGTH_LONG).show();
if(Philosopher.method==2) break;
philosopher_manager.set_methord(2);//设置防死锁方法2
break;
case R.id.m3:
Toast.makeText(MainActivity.this,"偶先左,奇先右",Toast.LENGTH_LONG).show();
if(Philosopher.method==3) break;
philosopher_manager.set_methord(3);//设置防死锁方法3
break;
case R.id.m4:
Toast.makeText(MainActivity.this,"先拿小编号",Toast.LENGTH_LONG).show();
if(Philosopher.method==4) break;
philosopher_manager.set_methord(4);//设置防死锁方法4
break;
}
}
}; public static void clear(){//清空显示
str.delete(0,str.length());
status.setText(str);
} private void start_stop(){//开始|暂停
if (philosopher_manager.is_start){
philosopher_manager.is_start=false;
start_stop.setBackgroundResource(R.drawable.start);
}else {
philosopher_manager.is_start=true;
start_stop.setBackgroundResource(R.drawable.stop);
}
} public static Handler handler=new Handler() {
@Override
public void handleMessage(Message msg) {
if (str.length()>10000){
str.delete(0,2000);//只留10000的缓存
}
status.setText(str);
scrollView.fullScroll(ScrollView.FOCUS_DOWN);//滚动到底部
}
}; public static void send_message(String s,int method){//发送消息
if (method==Philosopher.method){//过滤掉切换方法以前消息
str.append(s);
handler.sendEmptyMessage(method);
}
}
}

2.1.2布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.philosohper.MainActivity"> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:padding="10dp">
<Button
android:id="@+id/clear"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@drawable/clear"/>
<Button
android:id="@+id/start_stop"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@drawable/start"
android:layout_alignParentRight="true"/>
</RelativeLayout>
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="390dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="#f3efef">
<TextView
android:id="@+id/status"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:gravity="center"
android:padding="10dp">
<Button
android:id="@+id/m1"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_margin="7dp"
android:background="@drawable/m1"/>
<Button
android:id="@+id/m2"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_margin="7dp"
android:background="@drawable/m2"/>
<Button
android:id="@+id/m3"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_margin="7dp"
android:background="@drawable/m3"/>
<Button
android:id="@+id/m4"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_margin="7dp"
android:background="@drawable/m4"/>
</LinearLayout>
</RelativeLayout>

​ 界面效果如下:

2.2哲学家管理者类、哲学家类、筷子类

2.2.1哲学家管理者类

Philosopher_manager.java

package com.philosohper;

public class Philosopher_manager {
private Philosopher[] philosopher;
public static boolean is_start;//开始|暂停 Philosopher_manager(){
philosopher=new Philosopher[Chopsticks.N];
create();
is_start=false;
} private void create(){//创建哲学家线程
is_start=false;
Chopsticks.init();
for (int i=0;i<Chopsticks.N;i++){
philosopher[i]=new Philosopher(i);
philosopher[i].start();
}
} public void set_methord(int methord){//设置避免死锁方法
Philosopher.method=methord;
for (int i=0;i<Chopsticks.N;i++){
philosopher[i].exist=false;
}
boolean flag=is_start;
is_start=true;//让所有哲学家线程运行至结束
if (is_all_died()) {
create();
}
MainActivity.clear();//清楚显示
is_start=flag;
} private boolean is_all_died(){//判断所有哲学家线程是否运行结束
while(philosopher[0].isAlive()){
Chopsticks.init();
sleep();
}
while(philosopher[1].isAlive()){
Chopsticks.init();
sleep();
}
while(philosopher[2].isAlive()){
Chopsticks.init();
sleep();
}
while(philosopher[3].isAlive()){
Chopsticks.init();
sleep();
}
return true;
} private void sleep(){
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

2.2.2哲学家类

Philosopher.java

package com.philosohper;

public class Philosopher extends Thread{//哲学家类
private Method1 method1;
private Method2 method2;
private Method3 method3;
private Method4 method4;
public static int method=1;//所有哲学家使用的防死锁方法
public boolean exist=true;//标记线程是否存活 Philosopher(int num){//num为哲学家编号
method1=new Method1(num);
method2=new Method2(num);
method3=new Method3(num);
method4=new Method4(num);
} @Override
public void run() {
while(exist){
switch (method){
case 1:
method1.run();
break;
case 2:
method2.run();
break;
case 3:
method3.run();
break;
case 4:
method4.run();
break;
}
}
}
}

2.2.3筷子类

Chopsticks.java

package com.philosohper;

public class Chopsticks {//筷子对象,规定:0号筷子在0号哲学家的左边,哲学家和筷子都按逆时针编号
public static final int N=5;
public static int[] chopsticks; public static void init(){
chopsticks=new int[N];
for (int i=0;i<N;i++){
Chopsticks.chopsticks[i]=1;//1表示筷子资源个数
}
} public synchronized static int count(){//计算当前已被占用的筷子个数
int s=0;
for (int i=0;i<N;i++){
if (Chopsticks.chopsticks[i]<1){
s++;
}
}
return s;
}
}

2.3避免死锁的方案

本文采用4种方法避免死锁:最多允许4个哲学家拿起筷子、哲学家一次拿两支筷子、偶数编号的哲学家先拿左边的筷子奇数编号的哲学家先拿右边的筷子、所有哲学家先拿小编号的筷子。

2.3.1最多允许4个哲学家拿起筷子

Method1.java

package com.philosohper;

public class Method1{//最多允许4个哲学家拿起筷子
protected int num;//哲学家编号
protected int left,right;//num号哲学家左边、右边的筷子编号
protected final int wait_time=400;//等待时间
protected int method;//防止死锁的方法代号
protected boolean exit=true; Method1(int num) {
this.num=num;
left=num;//num号哲学家左边的筷子编号
right=(num+1)%Chopsticks.N;//num号哲学家右边的筷子编号
method=1;
} protected void run() {//最多允许4个哲学家拿起筷子
think();//哲学家在思考
P(left);
P(right);
eat();//哲学家在吃放
V(left);
V(right);
} protected synchronized void P(int i) {//P原语,申请筷子资源
wait_start();//等待游戏开始
while(Chopsticks.chopsticks[i]<=0||Chopsticks.count()>=Chopsticks.N-1&&i==left){//如果已经有4个人拿起了筷子,不允许手里没筷子的人拿筷子
block_thread();//阻塞线程
}
Chopsticks.chopsticks[i]--;
if (i==num){
MainActivity.send_message("" + num + "号哲学家拿起了左边的" + i + "号筷子\n",method);
}else{
MainActivity.send_message(""+num+"号哲学家拿起了右边的"+i+"号筷子\n",method);
}
} protected synchronized void V(int i){//V原语,释放筷子资源
wait_start();//等待游戏开始
Chopsticks.chopsticks[i]++;
if (i==num){
MainActivity.send_message("" + num + "号哲学家放下了左手的" + i + "号筷子\n",method);
}
else{
MainActivity.send_message("" + num + "号哲学家放下了右手的" + i + "号筷子\n",method);
}
} protected synchronized void think(){//哲学家在思考
wait_start();//等待游戏开始
MainActivity.send_message("" + num + "号哲学家在思考\n", method);
block_thread();//阻塞线程
} protected synchronized void eat(){//哲学家在吃放
wait_start();//等待游戏开始
MainActivity.send_message("" + num + "号哲学家握着左边" + left + "号和右边" + right + "号筷子在吃饭\n",method);
block_thread();//哲学家就餐一会儿
} protected void wait_start(){//等待游戏开始
while(!Philosopher_manager.is_start){
block_thread();//阻塞线程
}
} protected void block_thread(){//阻塞线程
try {
Thread.sleep(wait_time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

2.3.2哲学家一次拿两支筷子

Method2.java

package com.philosohper;

public class Method2 extends Method1 {//哲学家一次拿两支筷子

    Method2(int num) {
super(num);
method=2;
} protected void run() {//哲学家一次拿两支筷子
think();//哲学家在思考
P(left,right);
eat();//哲学家在吃放
V(left, right);
} protected synchronized void P(int i,int j) {//P原语,申请筷子资源
wait_start();//等待游戏开始
while(Chopsticks.chopsticks[i]<=0||Chopsticks.chopsticks[j]<=0){
block_thread();//阻塞线程
}
Chopsticks.chopsticks[i]--;
Chopsticks.chopsticks[j]--;
MainActivity.send_message("" + num + "号哲学家拿起了左边的" + i + "号和右边的" + j + "号筷子\n",method);
} protected synchronized void V(int i,int j){//V原语,释放筷子资源
wait_start();//等待游戏开始
Chopsticks.chopsticks[i]++;
Chopsticks.chopsticks[j]++;
MainActivity.send_message("" + num + "号哲学家放下了左手的" + i + "号和右手的" + j + "号筷子\n",method);
}
}

2.3.3偶数编号的哲学家先拿左边的筷子,奇数编号的哲学家先拿右边的筷子

Method3.java

package com.philosohper;

public class Method3 extends Method1 {//偶数编号的哲学家先拿左边的快在,奇数编号的哲学家先拿右边的筷子

    Method3(int num){
super(num);
method=3;
} @Override
protected void run() {//偶数编号的哲学家先拿左边的筷子,奇数编号的哲学家先拿右边的筷子
if (num%2==0){//偶数编号的哲学家先拿左边的筷子
think();//哲学家在思考
P(left);
P(right);
eat();//哲学家在吃放
V(left);
V(right);
}else {//奇数编号的哲学家先拿右边的筷子
think();//哲学家在思考
P(right);
P(left);
eat();//哲学家在吃放
V(left);
V(right);
}
} protected synchronized void P(int i) {//P原语,申请筷子资源
wait_start();//等待游戏开始
while(Chopsticks.chopsticks[i]<=0){
block_thread();//阻塞线程
}
Chopsticks.chopsticks[i]--;
if (i==num){
MainActivity.send_message("" + num + "号哲学家拿起了左边的" + i + "号筷子\n", method);
}
else{
MainActivity.send_message("" + num + "号哲学家拿起了右边的" + i + "号筷子\n",method);
}
} protected synchronized void eat(){//哲学家在吃放
wait_start();//等待游戏开始
if (num%2==0){
MainActivity.send_message("" + num + "号哲学家握着左边" + left + "号和右边" + right + "号筷子在吃饭\n",method);
}
else{
MainActivity.send_message("" + num + "号哲学家握着右边" + right + "号和左边" + left + "号筷子在吃饭\n",method);
}
block_thread();//阻塞线程
}
}

2.3.4所有哲学家先拿小编号的筷子

Method4.java

package com.philosohper;

public class Method4 extends Method1 {//所有哲学家先拿小编号的筷子

    Method4(int num) {
super(num);
method=4;
} protected void run() {//所有哲学家先拿小编号的筷子
if (num<Chopsticks.N-1){//0~N-2号哲学家先拿左边的小编号筷子
think();//哲学家在思考
P(left);
P(right);
eat();//哲学家在吃放
V(left);
V(right);
}else {//N-1号哲学家先拿右边的小编号筷子
think();//哲学家在思考
P(right);
P(left);
eat();//哲学家在吃放
V(left);
V(right);
}
} protected synchronized void P(int i) {//P原语,申请筷子资源
wait_start();//等待游戏开始
while(Chopsticks.chopsticks[i]<=0){
block_thread();//阻塞线程
}
Chopsticks.chopsticks[i]--;
if (i==num){
MainActivity.send_message("" + num + "号哲学家拿起了左边的" + i + "号筷子\n", method);
}else{
MainActivity.send_message("" + num + "号哲学家拿起了右边的" + i + "号筷子\n", method);
}
} protected synchronized void eat(){//哲学家在吃放
if (num<Chopsticks.N-1){
MainActivity.send_message(""+num+"号哲学家握着左边"+left+"号和右边"+right+"号筷子在吃饭\n",method);
}else{
MainActivity.send_message(""+num+"号哲学家握着右边"+right+"号和左边"+left+"号筷子在吃饭\n",method);
}
block_thread();//阻塞线程
}
}

3运行结果

3.1最多允许4个哲学家拿起筷子



3.2哲学家一次拿两支筷子



3.3偶数编号的哲学家先拿左边的筷子,奇数编号的哲学家先拿右边的筷子



3.4所有哲学家先拿小编号的筷子

4 拓展延伸

实际上,有很多种防死锁的方法,不仅仅局限于以上四种。下面从3个角度讨论:限制最大持筷子的哲学家数、限制哲学家拿筷子动作、限制哲学家拿筷子顺序。

4.1限制最大持筷子的哲学家数

方法一中,限制最多只允许4个哲学家同时持有筷子,我们也可以限制最多只允许3个哲学家同时持有筷子。当然,还可以限制最多只允许1个哲学家同时持有筷子,即哲学家排队就餐。

4.2限制哲学家拿筷子动作

方法二中,限制哲学家一次拿两支筷子。

4.3限制哲学家拿筷子顺序

当哲学家一次只能拿一支筷子时,只要不是所有哲学家都按相同的循序拿起筷子,就可以避免死锁。比如:方法四中,所有哲学家都先拿小编号的筷子,实际上就是0-3号哲学家先拿左边的筷子,4号哲学家先拿右边的筷子。我们也可以定义:1-4号哲学家先拿左边的筷子,0号哲学家先拿右边的筷子。再比如:方法三中,偶数编号的哲学家先拿左边的筷子,奇数编号的哲学家先拿右边的筷子,实际上就是0、2、4号哲学家先拿左边的筷子,1、3号哲学家先拿右边的筷子。我们也可以定义0、1、2号哲学家先拿左边的筷子,3、4号哲学家先拿右边的筷子。

综合以上三大类防死锁策略,我们也可以综合起来使用,比如:限制最多只允许4个哲学家同时持有筷子,0号哲学家一次只能拿2支筷子,1、2号哲学家先拿左边的筷子,3、4号哲学家先拿右边的筷子。

​ 声明:本文转自基于Android的“哲学家就餐问题”防死锁的设计与实现

基于Android的“哲学家就餐问题”防死锁的设计与实现的更多相关文章

  1. 基于Android 平台简易即时通讯的研究与设计[转]

    摘要:论文简单介绍Android 平台的特性,主要阐述了基于Android 平台简易即时通讯(IM)的作用和功能以及实现方法.(复杂的通讯如引入视频音频等可以考虑AnyChat SDK~)关键词:An ...

  2. 基于Android的小巫新闻客户端开发系列教程

    <ignore_js_op> 141224c6n6x7wmu1aacap7.jpg (27.51 KB, 下载次数: 0) 下载附件  保存到相册 23 秒前 上传   <ignor ...

  3. JAVA并发,经典死锁案例-哲学家就餐

    转自:http://blog.csdn.net/tayanxunhua/article/details/38691005 死锁经典案例:哲学家就餐. 这个案例会导致死锁. 通过修改<Java编程 ...

  4. 哲学家就餐问题-Java语言实现死锁避免

    哲学家就餐问题-Java语言实现死锁避免 我死锁预防是至少破坏死锁产生的四个必要条件之一,带来的问题就是系统资源利用率低且不符合开发习惯,而死锁避免不是事先釆取某种限制措施破坏死锁的必要条件,只是注意 ...

  5. JAVA多线程学习--哲学家就餐问题

    哲学家就餐问题是1965年由Dijkstra提出的一种线程同步的问题. 问题描述:一圆桌前坐着5位哲学家,两个人中间有一只筷子,桌子中央有面条.哲学家思考问题,当饿了的时候拿起左右两只筷子吃饭,必须拿 ...

  6. linux下多线程互斥量实现生产者--消费者问题和哲学家就餐问题

    生产者消费者问题,又有界缓冲区问题.两个进程共享一个一个公共的固定大小的缓冲区.其中一个是生产者,将信息放入缓冲区,另一个是消费者,从缓冲区中取信息. 问题的关键在于缓冲区已满,而此时生产者还想往其中 ...

  7. 基于android平台的斗地主AI

    本软件是基于android平台的斗地主AI,我们在源代码的基础之上,旨在改进AI的算法,使玩家具有更丰富的体验感,让NPC可以更为智能. (一)玩法解析: (1)发牌和叫牌:一副扑克54张,先为每个人 ...

  8. 基于Android Studio搭建hello world工程

    基于Android Studio搭建hello world工程 版本:ANDROID STUDIO V0.4.6 This download includes: ·        Android St ...

  9. 基于Android 4.4 开发的多窗体系统 开放源代码

    Hi, 这是我基于Android 4.4开发的多窗体系统,还有非常多不足,还请多多不吝赐教啊,代码已经所有开源. 视频地址 源代码地址 Done: 1. APP以窗体化显示 在 PhoneWindow ...

  10. 【转】基于 Android NDK 的学习之旅-----数据传输(引用数据类型)

    原文网址:http://www.cnblogs.com/luxiaofeng54/archive/2011/08/20/2147086.html 基于 Android NDK 的学习之旅-----数据 ...

随机推荐

  1. 状态: 失败 -测试失败: IO 错误: The Network Adapter could not establish the connection (CONNECTION_ID=BMRc/8PgR2+0i4PK2tnHQA==)

    1.问题 问题如标题所示,在使用Oracle SQL Developer连接时发现错误: 状态: 失败 -测试失败: IO 错误: The Network Adapter could not esta ...

  2. VMware虚拟机部署Linux Ubuntu系统的方法

      本文介绍基于VMware Workstation Pro虚拟机软件,配置Linux Ubuntu操作系统环境的方法.   首先,我们需要进行VMware Workstation Pro虚拟机软件的 ...

  3. [转帖]军备芯片14nm对比5nm,在战场上差距在哪里?

    https://www.eet-china.com/mp/a207185.html 现在全球已经打响科技之战,每个国家都在力求让自己做到足够拔尖.美国商务部长就曾自曝家底说,美国制定两套战略应对在芯片 ...

  4. 【转帖】关于网卡特性TSO、UFO、GSO、LRO、GRO

    https://www.cnblogs.com/larrypeng/p/12496810.html 我们来看下关于网卡特性的解释,不过记住GSO和GRO两个特性就好. TSO(TCP Segmenta ...

  5. jconsole的简单学习

    摘要 jconsole 是JDK自带的一款图形化监测工具 他可以监测本地程序,也可以检测远程的机器 在没有其他监控手段可以使用的情况下可以快速进行必要的监测 使用方法也比较简单. 本地监控 jcons ...

  6. 【图论】CF1508C Complete the MST

    Problem Link 有一张 \(n\) 个点的完全图,其中 \(m\) 条边已经标有边权.你需要给剩下的边都标上权值,使得所有边权的异或和为 \(0\),并且整张图的最小生成树边权和最小. \( ...

  7. 每日一库:Memcache

    Memcache 是一个高性能.分布式的内存缓存系统,常用于缓存数据库查询结果.API调用结果.页面内容等,以提升应用程序的性能和响应速度.下面详细介绍一些 Memcache 的特点和使用方式: 内存 ...

  8. Fabric配置块结构解析

    本文是区块链浏览器系列的第二篇. 上一篇介绍了交易块中的数据结构,这一篇介绍区块链网络中的配置块数据结构. 这两种区块中数据结构内容的区别主要Payload结构体中的Data域中的内容,接下来将以类图 ...

  9. 5.0 Python 定义并使用函数

    函数是python程序中的基本模块化单位,它是一段可重用的代码,可以被多次调用执行.函数接受一些输入参数,并且在执行时可能会产生一些输出结果.函数定义了一个功能的封装,使得代码能够模块化和组织结构化, ...

  10. C/C++ 简易异或加密的分析

    异或,加解密shellcode代码的简单分析方法. #include <stdio.h> #include <Windows.h> unsigned char buf[] = ...