📄 diningphils.java
字号:
import java.util.Random;
/*
* 题目:正在进餐的哲学家类。<br>
* 描述:主类,创建哲学家线程,并显示出当前哲学家和筷子的状态。
*
* @author 石庆华
*/
public class DiningPhils
{
/**
* 主函数,对线程进行初始化并运行。
*
* @param args 这是一个字符串数组,用来接收控制台输入的参数。
*/
public static void main ( String [] args )
{
final int n = 5; //5名哲学家
DiningPhils self = new DiningPhils ( ); //创建自己的实例
self.init ( n ); //初始化操作
}
/**
* 设置筷子的状态。<br>
* ture 时为拿起,false 时为放下。
*
* @param i 筷子数组的下标。
* @param v boolean类型,ture 时为拿起,false 时为放下。
*/
public void setChopstick( int i, boolean v )
{
chops[ i ] = v;
}
/**
* 得到筷子的状态。
*
* @param i 筷子数组中某个筷子的下标。
* @return 返回boolean类型;ture 时为拿起,false 时为放下。
*/
public boolean getChopstick( int i )
{
return chops [ i ];
}
/**
* 初始化操作。
* @param N 哲学家数目,final类型。
*/
private void init( final int N )
{
r = new Random ( );
n = ( N < 0 || N > maxPhils ) ? maxPhils : N; //不能超过最大哲学家数目
chops = new boolean [ n ]; //对筷子数组进行声明
phils = new Philosopher [ n ]; //对哲学家数组进行声明
initPhils ( ); //对哲学家数组初始化
dumpStatus ( ); //先是当前哲学家和筷子的状态
}
/**
* 对哲学家数组初始化的方法。<p>
* 对每一个哲学家进行初始化操作,并设置每一个哲学家的间隔时间(随机产生),最后每个
* 哲学家进程降低一级,使所有哲学家进程全部初始化完毕前不会有哲学家进程抢占主线程。
*/
private void initPhils()
{
for( int i = 0; i < n; i++ )
{
phils[ i ] = new Philosopher( this, i );
phils[ i ].setTimeSlice( generateTimeSlice ( ) ); //设置随机的间隔时间
phils[ i ].setPriority( Thread.NORM_PRIORITY - 1 );
//哲学家进程降低一级,使所有哲学家进程
//全部初始化完毕前不会有哲学家进程抢占主线程
}
while ( moreToStart ( ) ) //当还有没运行的线程时
{
int i = Math.abs( r.nextInt ( ) ) % n; //随机决定是哪一个哲学家
if( ! phils [ i ].isAlive ( ) ) //当没有开始活动时
{
System.out.println ( " Philosopher_ " +
String.valueOf( i ) + " started." );
phils[i].start ( ); //开始进程
}
}
System.out.println( "\nPhilosophers Chopsticks"
+ "\n(1 = eating, 0 = thinking) (1 = taken, 0 = free)" ); //状态提示
}
/**
* 产生一个随机的时间间隔。
*
* @return ts 随即产生的时间间隔(在maxEat和minEat之间)。当为零时,自动设置为最短进餐时间。
*/
public int generateTimeSlice ( )
{
int ts = Math.abs ( r.nextInt ( ) ) % ( maxEat + 1 );
if ( ts == 0 )
ts = minEat;
return ts;
}
/**
* 显示当前哲学家进餐状态和筷子的使用情况。<p>
* 1代表哲学家正在进餐反之正在思考,1代表筷子正在使用反之没有使用。
*/
public void dumpStatus()
{
for ( int i = 0; i < n; i++ ) //哲学家
System.out.print ( phils [ i ].getEat ( ) ? 1 : 0 );
for ( int i = n; i < maxPhils + 4; i++ )
System.out.print ( " " );
for ( int i = 0; i < n; i++ ) //筷子
System.out.print ( chops [ i ] ? 1 : 0 );
System.out.println ( );
}
/**
* 得到私有哲学家个数的公共方法。
*
* @param n 哲学家的个数。
* */
public int getCount()
{
return n; //哲学家个数
}
/**
* 判断是否还有没有启动的哲学家。
*
* @return boolean 类型,<br>ture 表示还有没有启动的哲学家线程,<br>false 表示已经全部启动
*/
private boolean moreToStart()
{
for( int i = 0; i < phils.length; i++ )
{
if ( !phils [ i ].isAlive ( ) ) //若有没有在运行的线程
return true; //返回true
}
return false; //否则返回false
}
private int n; //哲学家个数
private Philosopher[] phils;
private boolean[] chops;
private Random r;
private static final int maxPhils = 24; //最多哲学家数
private static final int maxEat = 4; //最多进餐时间
private static final int minEat = 1; //最少进餐时间
}
/**
* 题目:哲学家类。<br>
* 描述:继承了Thread线程类,覆盖了Thread 类的run()方法,可以完成think(),eat()等方法。
*
* @author 石庆华
*
*/
class Philosopher extends Thread
{
/**
* Philosopher 类的构造函数。
*
* @param HOST DiningPhils 类型的参数,
* @param i 哲学家标号。
*/
public Philosopher( DiningPhils HOST , int i )
{
host = HOST;
philo_num = i;
}
/**
* 吃饭方法。<p>
* 线程暂停一段时间,然后设置吃饭状态为false,放下筷子。
*/
private void eat()
{
pause();
setEat( false );
putDownChops();
}
/**
* 思考的方法。<p>
* 调用暂停函数,线程暂停一段时间。
*/
private void think()
{
pause ( );
}
/**
* 覆盖了Thread类中run()函数,启动线程运行的方法。<p>
* 按顺序分别执行拿起筷子,吃饭,思考的方法。
*/
public void run()
{
while(true)
{
grabChopsticks();
eat();
think();
}
}
/**
* 设置时间间隔的方法。
*
* @param TS 随即产生的时间参数。
*/
public void setTimeSlice( int TS )
{
ts = TS;
}
/**
* 判断哲学家左右筷子是否都可用的方法。<p>
* 只有左右两边的筷子都可用时才可以拿起两边的筷子,这样可以防止死锁的发生。
*
* @return boolean 类型的值。
* ture 表示哲学家左右两边的筷子都可用,false表示至少有一个不可用。
*/
public boolean chopsticksFree()
{
return !host.getChopstick( philo_num ) &&
!host.getChopstick ( ( philo_num + 1 ) % host.getCount ( ) );
}
/**
* 设置哲学家左边筷子状态的方法。
*
* @param flag 传入的boolean类型的状态参数,ture表示被拿起,false表示未使用。
*/
public void setLeftChopstick( boolean flag )
{
host.setChopstick ( philo_num , flag );
//调用正在进餐的哲学家类,设置当前正在进餐的哲学家的右筷子状态
}
/**
* 设置哲学家右边筷子状态的方法。
*
* @param flag 传入的boolean类型的状态参数
*/
public void setRightChopstick( boolean flag )
{
host.setChopstick ( ( philo_num + 1 ) % host.getCount ( ) , flag );
//调用正在进餐的哲学家类,设置当前正在进餐的哲学家的左筷子状态
}
/**
* 放下筷子的方法,将左右筷子的状态都设置为放下状态。
*/
private void putDownChops()
{
setLeftChopstick ( false ); //设为放下状态
setRightChopstick ( false );
}
/**
* 临界区函数,确保哲学家在没有筷子或筷子不够时思考,满足条件后才就餐。<br>
* 此方法是关键代码,不允许两个线程同时访问该部分。
*/
private synchronized void grabChopsticks()
{
while( !chopsticksFree ( ) ) //判断左右两边的筷子是否都可用,若其中有一个
//筷子不可用则将那块泽的操作挂起,直到两边都可用为止
{
try
{
wait();
}
catch( InterruptedException e){}
}
takeChopsticks(); //拿起两边的筷子
notifyAll(); //唤醒所有调用该对象wait()的哲学家,等待时间最长
//的哲学家得到该线程对象的锁
}
/**
* 拿筷子的方法。<p>
* 将左右筷子设为不可用,并设定吃饭状态,显示此时的哲学家和筷子的状态。
*/
private void takeChopsticks()
{
setLeftChopstick( true );
setRightChopstick( true );
setEat(true);
host.dumpStatus(); //显示状态
}
/**
* 线程暂停方法。<br>
*/
private void pause ()
{
setTimeSlice ( host.generateTimeSlice ( ) );
try
{
sleep ( ts*1000 );
}
catch ( InterruptedException e ){ }
}
/**
* 设置吃饭的状态。
*
* @param v boolean 类型的参数,ture为正在吃饭,反之为false.
*/
private void setEat ( boolean v )
{
isEating = v;
}
/**
* 得到吃饭状态。
*
* @return isEating boolean类型的参数,ture为正在吃饭,反之为false.
*/
public boolean getEat()
{
return isEating;
}
private DiningPhils host;
private boolean isEating;
private int philo_num;
private int ts;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -