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. 【js】 Object.prototype.toString.call()

    1,Object.prototype.toString这个方法的作用是什么  判断数据类型 2,为什么要用这个方法 是因为  js 中 一般的类型判断 对于 null,数组,对象 , 都会返回一样的结 ...

  2. WebApi允许跨域

    services.AddCors(options => { options.AddPolicy("abc", builder => { //App:CorsOrigin ...

  3. Python 变量?对象?引用?赋值?一个例子解释清楚

    哈喽大家好,我是咸鱼. 前天有个小伙伴找到我,给了我一段 python 代码: a = [1, 2] a[1] = a print(a[1]) 然后问我为什么结果是 [1, [...]],我一看这个问 ...

  4. [转帖]idea配置tomcat参数,防止nvarchar保存韩文、俄文、日文等乱码

    描述下我的场景: 数据库服务器在远程机器上,数据库使用的Oracle,字符集是ZHS16GBK,但保存韩文.俄文.日文等字段A的数据类型是nvarchar(120),而nvarchar使用的是Unic ...

  5. [转帖]是什么让 Redis“气急败坏”回击:13 年来,总有人想替 Redis 换套新架构

    https://www.infoq.cn/article/AlF5NIhHdskayl0MTyQG 回击就代表输了?! 今年年中,一位前谷歌.前亚马逊的工程师推出了他创作的开源内存数据缓存系统 Dra ...

  6. [转帖]使用 TiUP 部署运维 TiDB 线上集群

    https://docs.pingcap.com/zh/tidb/stable/tiup-cluster 本文重在介绍如何使用 TiUP 的 cluster 组件,如果需要线上部署的完整步骤,可参考使 ...

  7. [转帖]使用 goofys 挂载 S3 bucket 为文件系统

    https://xie.infoq.cn/article/7f178e0a1315f758d77c6c2bb 背景 公司的 gitlab 目前都是直接存储在物理盘上,为了确保数据不会丢失,需要重复多次 ...

  8. [转帖]Linux后门的几种姿势

      转载自 https://evilanne.github.io/2017/08/26/Linux后门-持续关注/ 在一次渗透中,成功获取某目标几台比较重要的机器,当时只想着获取脱库,结果动静太大被发 ...

  9. [转帖]远超DDR4,速度可达DDR5 6400!DDR5内存技术、产品解析

    https://www.sohu.com/a/326810241_616364 内存是计算机技术的重要组成部分,经历了长时间的竞争更替和路线选择之后,PC内存技术被稳定在以DDR技术为基础的发展路线上 ...

  10. Linux上面批量更新SQLSERVER SQL文本文件的办法

    1. 今天同事让帮忙更新几个SQL文件.. 本着自己虽然low 但是不能太low的想法, 简单写一个 shell 脚本来执行. 2. 因为我的linux 里面都安装了 sqlcmd 的工具 所以办法就 ...