《操作系统课程设计实验报告proj1.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计实验报告proj1.docx(25页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、操作系统课程设计报告操作系统课程设计报告班级:班级:团队成员团队成员:目录目录.2一、Nachos 综述.错误!未定义书签。错误!未定义书签。二、实验要求:建立线程系统.错误!未定义书签。错误!未定义书签。2.1 Task 1.1 实现 KThread.join().32.1.1 题目要求.32.1.2 题目分析与实现方案.32.1.3 关键点与难点.42.1.4 实现代码.42.2 Task 1.2 实现条件变量.52.2.1 题目要求.52.2.2 题目分析与实现方案.52.2.3 关键点与难点.62.2.4 实现代码.72.3 Task 1.3 完成 Alarm 类.72.3.1 题目要
2、求.72.3.2 题目分析与实现方案.82.3.3 关键点与难点.92.3.4 实现代码.92.4 Task 1.4 条件变量实现同步消息.102.4.1 题目要求.102.4.2 题目分析与实现方案.102.4.3 关键点与难点.112.4.4 实现代码.112.5 Task 1.5 实现优先级调度.132.5.1 题目要求.132.5.2 题目分析与实现方案.132.5.3 关键点与难点.142.5.4 实现代码.152.6 Task 1.6 条件变量解决过河问题.162.6.1 题目要求.162.6.2 题目分析与实现方案.172.6.3 关键点与难点.192.6.4 实现代码.19三、
3、测试结果.2 错误!未定义书签。错误!未定义书签。一、一、Nachos 综述综述Nachos 是一款教学用的操作系统平台,他的全名叫做“Not Another Completely Heuristic Operating System”,Nachos 的运行必须借助于宿主机,它通过软件模拟了各种硬件系统,包括中断系统、存储系统、磁盘文件、网络等。它的运行是可以跟踪的,因此,我们可以一步一步的观察操作系统是如何运行的。Nachos操作系统本身只提供了一套框架,很多地方的实现都需要我们自己来完善,因此我们可以通过修改其源代码,来丰富和增强Nachos 操作系统的功能.更可以在完善这些功能的同时,了
4、解操作系统的内部运行机制。二、实验要求:建立线程系统二、实验要求:建立线程系统2.12.1 TaskTask 1.11.1:实现:实现 KThread.join()KThread.join()2.11 题目要求实现 Implement Kthread.join()函数。其它的线程不必调用 join 函数,但是如果 join()函数被调用的话,也只能被调用一次。对 join()函数第二次调用的执行结果是不被定义的,即使第二次调用的线程与第一次调用的线程不同。无论有没有被 join,一个进程都能够正常结束。2.1.2 题目分析与实现方案分析:join 函数的作用即为等待调用此函数线程运行完毕。当前
5、线程 A 调用另一个线程(处于就绪状态)B 的 join 函数时,即 A 执行B.join()时(A和B在Nachos中均为Kthread类型对象),A被挂起,直到B 运行结束后,join 函数返回,A 才能继续运行。如果这个线程已经结束,马上返回。且 join()函数只能被调用一次,第二次调用不能保证返回,且调用 join()函数的线程必须不能是当前线程。Join()函数结束时唤醒在等待队列中的所有线程,因此需要实现 join()方法和修改finish()方法。方案:(1)Kthread 的 join()中的 Lib.assertTrue(this!=currentThread)已经实现线程
6、只能调用一次 join()方法,根据要求,在调用 join()方法时,让当前运行线程休眠,并将当前运行的线程加入到一个阻塞队列中。(2)在线程结束时,finish()函数循环唤醒所有被阻塞的线程。Finish()函数在 run()函数返回时自动调用,同样可以被直接调用。但当前唯一运行的线程不能被 finish()函数立即结束,只有当其他线程运行时才可结束。2.1.3 关键点与难点由于 java 派生于抽象类的对象无法实例化,在运行时很可能出现空指针异常。只能直接利用抽象类的对象调用相关方法。2.1.4 实现代码public void join()Lib.debug(dbgThread,“Joi
7、ning to thread:“+toString();/判断要调用的进程是否为当前进程(线程只能调用一次 join()方法)Lib.assertTrue(this!=currentThread);/系统关中断,当前线程休眠oolean intStatus=Machine.interrupt().disable();if(status!=statusFinished)/调用另一个要调用的进程,将当前运行的线程加入到一个阻塞队列中。waitForJoin.waitForAccess(currentThread);/当前进程睡眠等待被调用进程结束sleep();Machine.interrupt(
8、).restore(intStatus);public static void finish()Lib.debug(dbgThread,“Finishing thread:“+currentThread.toString();/系统关中断Machine.interrupt().disable();/当前进程运行结束的标志Machine.autoGrader().finishingCurrentThread();Lib.assertTrue(toBeDestroyed=null);/表示当前进程要结束了toBeDestroyed=currentThread;/当前进程的状态被修改为运行结束cur
9、rentThread.status=statusFinished;/调用等待队列上的第一个进程Kthread waitThread;while(waitThread=currentThread.waitForJoin.nextThread()!=null)/唤醒等待队列上所有被阻塞的进程waitThread.ready();sleep();2.2.2 2 TaskTask 1.21.2 实现条件变量实现条件变量2.2.1 题目要求利用中断有效和无效所提供的原子性直接实现条件变量。我们已经提供类似的例子实例实现信号量。你要按此提供类似的条件变量的实现,不能直接利用信号量来实现(你可以使用 loc
10、k,虽然它间接地调用了信号量)。在你完成时要提供条件变量的两种实现方法。你的第二种条件变量实现要放在 nachos.threads.Condition2 中。2.2.2 题目分析与实现方案分析:条件变量使我们可以睡眠等待某种条件出现,是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。方案:threads.Lock 类提供了锁以保证互斥。在临界代码区的两端执行Lock.acquire()和Lock.release()即可保证同时只有一个线程
11、访问临界代码区。条件变量建立在锁之上,由 threads.Condition 实现,用来保证同步。每一个条件变量拥有一个锁变量。(1)sleep()在条件变量的控制下 sleep()函数自动释放相关锁并进入睡眠状态,直到令一个线程用 wake()唤醒它,需要在函数的开始和结尾处关或允许中断保证原子性。当前线程必须拥有相关锁,阻塞进程在 sleep()函数返回之前线程将会自动再次拥有锁。即调用这个方法的线程将自己挂起到等待队列,阻塞自己的同时将自己拥有的锁交出去,之后等待锁的分配。拿到锁的线程再次拥有权限接受检验。(2)wake()唤醒条件变量队列中第一个线程,在试验中利用从线程队列中取出线程用
12、 Kthread.ready()实现唤醒,加入就绪队列。(同样要在wake 函数利用中断保证原子性)(3)wakeall()函数的实现依赖于 wake()。只需不断地 wake 挂在条件变量上的线程直到队列为空为止。2.2.3 关键点与难点题目要求直接实现条件变量,不能直接利用信号量。所以互斥锁必须要有,它是线程们有秩序的接受条件变量检验的保证。等待队列必须要有,不满足条件的线程都要挂在上面。2.2.4 实现代码public static void sleep()Lib.debug(dbgThread,“Sleeping thread:“+currentThread.toString();Li
13、b.assertTrue(Machine.interrupt().disabled();if(currentThread.status!=statusFinished)/锁住当前线程currentThread.status=statusBlocked;/运行下一个线程runNextThread();public void wake()Lib.assertTrue(conditionLock.isHeldByCurrentThread();if(!waitQueue.isEmpty()(Semaphore)waitQueue.removeFirst().V();public void wakeA
14、ll()Lib.assertTrue(conditionLock.isHeldByCurrentThread();while(!waitQueue.isEmpty()wake();2.2.3 3 TaskTask 1.31.3 完成完成 AlarmAlarm 类类2.3.1 题目要求完成 Alarm 类,通过 waitUntil(long x)方法实现。一个线程通过调用 waitUntil(long x)方法将自己挂起,一直到经过 x 时间再被唤醒。这对现实操作很有用,例如,光标的周期闪烁。线程经过 x 时间后唤醒,但不需要立刻运行,而是加入 readyqueue 中。不建立任何多余的线程实现
15、 waitUntil(),只需要修改 waitUntil()中断处理程序和计时器。waitUntil()不受限于任何一个线程,任何线程可以调用它实现被挂起。2.3.2 题目分析与实现方案分析:Alarm 类使用硬件定时器提供抢占,并允许线程挂起到某个时间。分配的新闹钟设置机器的定时器中断处理程序实现本闹钟的回调,同时 Nachos 只在有一个计时器的情况下正常运行。定时器中断处理程序被称为机器计时器定期(大约每 500 时钟周期)。当前线程产生后,如果有另一个必须运行的线程,则强制上下文切换。当前线程睡眠至少 x 时间周期,在定时器中断处理程序中将其唤醒。当现在时间(current time)
16、=(WaitUntil called time)+(x)时,线程必须被唤醒(在调度准备集)在第一个定时器中断的产生时。方案:(1)与 Alarm 类有关的是 machine.Timer 类,它在大约每 500 个时钟滴答使调用回调函数(由 Timer.setInterruptHandler 函数设置)。因此,Alarm 类的构造函数中首先要设置该回调函数Alarm.timerInterrupt()。timerInterrupt()方法在每一次 timer 产生时间中断时遍历队列,检查队列中的时间状态,当线程到了等待的时间就把线程从队列中取出放入就绪队列。(2)waitUntil()方法使用了一
17、个队列可以存放线程以及唤醒时间,这个队列以时间为序的有序队列。每次调用时,把当前线程和唤醒时间加入队列,等待唤醒。在调用 waitUntil(x)函数时,首先得到关于该线程的信息(线程:当前线程,唤醒时间:当前时间+x),该线程设定唤醒时间并阻塞,并调用 sleep 操作使当前线程挂起,放入队列中。在时钟回调函数中(大约每 500 个时钟间隔调用一次)有一次中断,中断时则依次检查队列中的每个对象,将队列中此时应当唤醒的进程加入就绪队列。如果唤醒时间大于当前时间,则将该对象移出队列并执行 wake 操作将相应线程唤醒。(3)Waiter()是一个用来管理和存储多个 Kthread 及其唤醒时间的
18、内部类,属性分别为该 Kthread 与其唤醒时刻 waketime。2.3.3 关键点与难点线程调用 waitUntil 方法之后会终止,该线程设定唤醒时间并阻塞,直到传入的时间之后才可以执行。线程只是放入就绪队列,等待分配。可以使用一个线程队列,但是不能产生额外的线程。Timer 每 过 500 个 clock ticks 会 产 生 一 个 timeInterrupt。timeInterupt 时,会运行 handle()线程,将 handler 线程功能设置为将队列中此时应当唤醒的进程加入就绪队列。2.3.4 实现代码class Waiter Waiter(long wakeTime,
19、Kthread thread)this.wakeTime=wakeTime;this.thread=thread;private long wakeTime;/唤醒时间private Kthread thread;/等待的线程private LinkedList waitlist;/存放线程以及唤醒时间public void waitUntil(long x)/系统关中断oolean intStatus=Machine.interrupt().disable();/for now,cheat just to get something working(busy waiting isbad)/确
20、定唤醒的时间long wakeTime=Machine.timer().getTime()+x;Waiter waiter=new Waiter(wakeTime,Kthread.currentThread();/将线程加入到等待队列上waitlist.add(waiter);System.out.println(“线程:”+Kthread.currentThread().getName()+“t 休眠时间:”+Machine.timer().getTime()+“t 下次唤醒时间:”+wakeTime);/当前线程设定唤醒时间并阻塞,挂起Kthread.sleep();/开中断Machine
21、.interrupt().restore(intStatus);2.2.4 4 TaskTask 1.41.4 条件变量实现同步消息条件变量实现同步消息2.4.1 题目要求使用条件变量来实现一个字长信息的发送和接收同步。使用 voidspeak(int word)和 int listen()函数来实现通讯(Communicator)类的通讯操作。Speak 函数具有原子性,在相同地 Communicator 类中等待 listen 函数被调用,然后将此字发生给 listen 函数。一旦传送完毕,两个函数都返回(listen 函数返回此字)。2.4.2 题目分析与实现方案解决方案要满足使用同一个
22、通讯对象实例中多个 speaker 和listener 能够相互通讯。(注意:这种情况等于 0 字长的缓冲区;既然缓冲区没用空间,那么需要生产者和消费者直接进行交互,要求他们相互等待)。每一个通讯实例只能使用一个 lock 类,如果使用多于一个 lock 类,会将事情复杂化。每个 Communicator 拥有的锁(保证操作的原子性)和与该锁联系的两个条件变量用于保证 speaker 和 listener 间的同步。在 speak 函数中,首先检查是否已经有 speaker 在等待(speaknum0)或无 listener等待,满足这两种情况就挂起。若不满足,则设置变量,准备数据并唤醒一个
23、listener。在 listen 函数中,增加一个 listener 后,首先,然后这个问题其实是一个缓冲区长度为 0 的生产者/消费者问题。Speak():先获得锁,然后进行判断,如果没有听者等待,就要把说者放入队列然后睡眠。如果有听者等待,就要唤醒一个听者,然后传递消息,最后释放锁。Listen():先获得锁,然后进行判断尝试唤醒 speaker,如果没有说者等待,就要把听者放入队列然后睡眠。如果有说者等待,就要唤醒一个说者,将自己挂起以等待 speaker 准备好数据再将自己唤醒,然后传递消息,最后释放锁。2.4.3 关键点与难点当有多个 speaker 和 listener 时,它们
24、也只能是一对一的,即一个speaker只能将数据发送到一个listener,一个listener也只能接收来自一个 speaker 的数据,其余的 speakers 和 listeners 都需要等待。多个speaker 阻塞时需要保存每一个 speaker 的 word,阻塞的 speaker 挂在Condition 对象的 Queue 队列上。因为无缓冲区,listener 唤醒说者后要先挂起,消息在 speaker 准备好后才传递。2.4.4 实现代码public class Communicator public Communicator()lock=new Lock();speake
25、r=new Condition2(lock);listener=new Condition2(lock);private Lock lock;/互斥锁private int word=0;private static int speakercount=0;/说者数量private static int listenercount=0;/听者数量LinkedList Wordqueue=new LinkedList();/保存说者话语的队列Condition2 speaker;/说者条件变量Condition2 listener;/听者条件变量/*先获得锁,然后进行判断,如果没有听者等待,就要把
26、说者放入队列然后睡眠。如果有听者等待,就要唤醒一个听者,然后传递消息,最后释放锁*/public void speak(int word)/系统关中断oolean intStatus=Machine.interrupt().disable();/拿到锁lock.acquire();/没有听者,说者睡眠,队列存储说者的话if(listenercount=0)speakercount+;Wordqueue.offer(word);speaker.sleep();/尝试唤醒听者listener.wake();/说者队列人数减一speakercount-;else/准备消息,唤醒听者Wordqueue
27、.offer(word);listener.wake();/释放锁lock.release();/系统开中断Machine.interrupt().restore(intStatus);System.out.println(Kthread.currentThread().getName()+“发出信息“+word);return;public int listen()/系统关中断oolean intStatus=Machine.interrupt().disable();/获得锁lock.acquire();if(speakercount!=0)speaker.wake();listener.
28、sleep();else listenercount+;listener.sleep();listenercount-;System.out.println(Kthread.currentThread().getName()+“收到信息“+Wordqueue.getFirst();System.out.println();lock.release();Machine.interrupt().restore(intStatus);return Wordqueue.poll();2.52.5 TaskTask 1.51.5 实现优先级调度实现优先级调度2.5.1 题目要求通过完成实现 Priori
29、tyScheduler 优先级调度策略。所有的调度程序 都 是 继 承 自 Scheduler 类,所 以 必 须 实 现 getPriority(),getEffectivePriority()和 setPriority()方法。2.5.2 题目分析与实现方案分析:Nachos 系统已经提供了一个简单的轮转调度器,采用简单的 FIFO 队列进行调度。优先级调度的传统算法如下:每个线程拥有一个优先级(在 Nachos 中,优先级是一个 0 到 7 之间的整数,默认为1)。在线程调度时,调度程序选择一个拥有最高优先级的处于就绪状态的线程运行。这种算法的问题是可能出现“饥饿”现象。为解决上述优先级
30、反转问题,需要实现一种“让出”优先级的机制(PriorityDonation)。getEffectivePriority()就是为了解决优先级翻转而设的,也就是说当出现一个优先级高的线程等待低优先级线程时,若存在另外的高优先级线程而导致低优先级线程永远不能执行的情况,该低优先级线程调用该函数返回它持有的锁上等待的所有线程中优先级中最高的一个给该优先级线程,以确保它执行。方案:在引入优先级的同时就需要引入一个 ThreadState 对象,它用来把线程和优先级绑定在一起。而且内部还有一个队列用来记录等待它的线程。在 ThreadState 类中增加两个 LinkedList类表,存放Priori
31、tyQueue。一个表用来记录该线程所占用资源的优先队列,另一个表用来记录该线程所想占有的资源的优先队列。占用资源的队列作为发生优先级反转时,捐献优先级计算有效优先级的来源依据,想占有 的 资 源 的 队 列 用 来 为 线 程 声 明 得 到 资 源 做 准 备。getEffectivePriority():计算有效优先级时,遍历等待队列中所用线程的有效优先级,找出最大的优先级。首先要在 PriorityQueue 类下创建个 装 有 Kthread 的 LinkedList,即 等 待 队 列 waitQueue,声 明 一 个effectivePriority,遍历 waitQueue,
32、找出 priority 最大的那个 Kthread,将它的 priority 赋给 effectivePriority,然后返回即可。而在队列类中最重要的就是 nextThread()方法,它返回下一个要执行的线程。这个方法通过遍历队列,计算出所有线程的有效优先级,取出有效优先级最大的线程执行。waitForAccess()将需要等待获得资源的线程加入一个等待队列等待调度。2.5.3 关键点与难点解决这个问题的关键是计算有效优先级,在改变优先级的时候计算。优先级在捐献之后不会丢失并可以不断传递下去。在PriorityScheduler 类的内部类。ThreadState 类里有 priorit
33、y 这属性,所以实现 getPriority(),setPriority(),increasePriority(),decreasePriority()时直接对 priority 进行返回,重新赋值,修改 priority 的值。(注意:getPriority(),setPriority()函数内部是先调用内部类 ThreadState 类的同名函数,然后在这些同名函数里对 priority 修改)。2.5.4 实现代码protected class ThreadState public ThreadState(Kthread thread)this.thread=thread;setPrio
34、rity(priorityDefault);public int getPriority()return priority;public int getEffectivePriority()return priority;public void setPriority(int priority)if(this.priority=priority)return;this.priority=priority;public void waitForAccess(PriorityQueue waitQueue)if(waitQueue.holder!=null)if(this.priority wai
35、tQueue.holder.priority)waitQueue.holder.priority=this.priority;waitQueue.waitQueue.add(this);public void acquire(PriorityQueue waitQueue)/implement meLib.assertTrue(Machine.interrupt().disabled();Lib.assertTrue(waitQueue.waitQueue.isEmpty();waitQueue.holder=this;protected Kthread thread;protected in
36、t priority;public Kthread nextThread()Lib.assertTrue(Machine.interrupt().disabled();/implement meif(waitQueue.isEmpty()return null;int max_priority=0;int index=0;int m=0;for(Iterator I=waitQueue.iterator();i.hasNext();)ThreadState o=(ThreadState)i.next();if(o.priority max_priority)index=m;max_priori
37、ty=o.priority;m+;ThreadState nextState=waitQueue.remove(index);if(holder!=null)holder=nextState;return nextState.thread;2.62.6 TaskTask 1.61.6 条件变量解决过河问题条件变量解决过河问题2.6.1 题目要求利用前面所学同步方法,以及条件变量解决过河问题。一群夏威夷人想要从瓦胡岛(Oahu)到摩洛凯岛(Molokai),只有一艘船,该船可载最多 2 个小孩或一个成人,且必须有一个开船的人(默认每个人都会开船)。设计一个解决方案,使得每个人都能从 Oahu 到
38、Molokai。假设至少有两个小孩,每一个人都会划船。要为每个大人和孩子创建一个线程,这个线程就相当于一个人,他们能自己判断(思考),什么时候可以过河,而不是通过调度程序的调度。在解决问题的过程中不能出现忙等待,而且问题最终一定要解决。不符合逻辑的事情不能发生,比如在这个岛的人能知道对面岛的情况。2.6.2 题目分析与实现方案需要记录的信息有:1.O 岛上大人/小孩的人数和 M 岛上大人/小孩的人数1)船的位置(在 O 岛还是 M 岛)和状态(空/半满/全满)(半满指只有一个 小孩,全满指有两个小孩或一个大人)2.是否最后一次运输3.孩子、大人信息的条件变量,并且需要一个锁运送过程大体如下:1
39、:所以我们可以尽可能多的先从 O 岛把孩子运过来(M 岛到 O岛需要一个 child 驾驶员,要避免出现当大人到 M 岛上而没有孩子在M 岛上出现这种情况)2:然后开始运输大人,并且让孩子把船开回 O 岛3:重复 1,24:最后将两个孩子运到 M 岛对于大人,若满足以下条件则独自乘船过河(每个大人过且仅过一次河,线程即告结束),否则(在 O 岛)等待:2)O 岛上只有一个小孩或没有小孩3)2)船在 O 岛4)3)船为空对于小孩,满足以下情况则乘船过河:1)初始条件,小孩全在 O 岛,船在 O 岛,且 M 岛没有小孩。2)某小孩在O岛,船在O岛,船为空,O岛上的小孩数大于等于2,则该小孩上船等另
40、外一个小孩上船后,两人一起划船过河到 M。3)某小孩在 O 岛,船在 O 岛,船为空,O 岛上没有大人:该小孩上船过河4)当所有的大人运完了之后,O 岛出现了两个孩子,这个时候这两个孩子划船过河。方案:使用一个锁变量保证互斥,四个条件变量保证同步。结构如下:程序首先创建大人线程和孩子线程获取锁唤醒一个孩子进程释放锁。ChildItinerary()思路:先 获 得 锁conditionLock.acquire()并 处 于 等 待 状 态childOnOahu.sleep();判断是否为最后一次运输若是最后一次,直接进行运输并结束。若不是,则判断是否在 Oahu。若在,O 岛孩子数减一。判断是
41、否有驾驶员,若有,直接进行运输 ChildRideToMolokai();若没有驾驶员,他将成为驾驶员并唤醒一个孩子线程。若孩子在 M 岛,则将船开回 O 岛,O 岛孩子数量改变。如果 O岛孩子数为 1 则唤醒大人线程,准备运送大人,否则唤醒孩子线程,继续运送孩子。AdultItinerary()思路:与 ChildItinerary 思想类似,但少了返回 O岛这一步。大体为获得锁 conditionLock.acquire()并进入等待状态adultOnOahu.sleep(),直接去 M 岛然后唤醒 M 岛一个孩子。2.6.3 关键点与难点难点在渡河过程分析。只有小孩可以有返程路线,大人返
42、程没有意义;要避免出现当大人到 M 岛上而没有孩子在 M 岛上出现这种情况。开始之前让所有人 sleep,然后 begin()叫醒一个 child,然后每次一个人干完了活叫醒另一个然后自己睡,所有人都是被动的,相比所有人都主动竞争当驾驶员或乘客,这样可以避免冲突。2.6.4 实现代码public class Boat public static void selfTest(int i,int j)BoatGrader b=new BoatGrader();childrenOnOahuBG=j;childrenOnMolokaiBG=0;adultOnOahuBG=i;adultOnMoloka
43、iBG=0;System.out.println(j+children and +i+adults.);begin(i,j,b);public static void begin(int adults,int children,BoatGrader b)bg=b;lock1=new Lock();childrenWaitOnOahu=new Condition(lock1);lock2=new Lock();adultWaitOnOahu=new Condition(lock2);lock3=new Lock();childrenReadyOnMolokai=new Condition(loc
44、k3);for(int i=0;i adults;i+)new KThread(new Adult(childrenWaitOnOahu,adultWaitOnOahu,childrenReadyOnMolokai).setName(adult).fork();for(int i=0;i 1)/有孩子lock1.acquire();childrenWaitOnOahu.wake();pilot=1;/运孩子bg.ChildRowToMolokai();childrenOnOahu-;childrenOnMolokai+;lock1.release();lock3.acquire();/回来一个
45、孩子childrenReadyOnMolokai.sleep();lock3.release();else/运大人lock2.acquire();adultWaitOnOahu.wake();lock2.release();lock1.acquire();childrenWaitOnOahu.sleep();lock1.release();continue;else if(adultOnOahu!=0)bg.ChildRideToMolokai();childrenOnOahu-;childrenOnMolokai+;lock3.acquire();childrenReadyOnMolokai
46、.wake();lock3.release();lock3.acquire();childrenReadyOnMolokai.sleep();lock3.release();else/剩下的孩子从 O 岛到 M 岛lock3.acquire();done=true;bg.ChildRideToMolokai();childrenOnOahu-;childrenOnMolokai+;childrenReadyOnMolokai.wakeAll();lock3.release();/返航的操作if(done=true)break;/表示没有结束else pilot=3;bg.ChildRowToO
47、ahu();childrenOnOahu+;childrenOnMolokai-;continue;static void SampleItinerary()System.out.println(n Everyone piles on the boat and goes toMolokai);bg.AdultRowToMolokai();bg.ChildRideToMolokai();bg.AdultRideToMolokai();bg.ChildRideToMolokai();private static class Child implements Runnable Child(Condi
48、tion childrenWaitOnOahu,Condition adultWaitOnOahu,Condition childrenReadyOnMolokai)this.location_now=location_now;this.childrenWaitOnOahu=childrenWaitOnOahu;this.adultWaitOnOahu=adultWaitOnOahu;this.childrenReadyOnMolokai=childrenReadyOnMolokai;public void run()ChildItinerary();private int Status;pr
49、ivate int location_now;/1:Oahu,2:Molokaiprivate Condition childrenWaitOnOahu;private Condition adultWaitOnOahu;private Condition childrenReadyOnMolokai;private static class Adult implements Runnable Adult(Condition childrenWaitOnOahu,Condition adultWaitOnOahu,Condition childrenReadyOnMolokai)this.childrenWaitOnOahu=childrenWaitOnOahu;this.adultWaitOnOahu=adultWaitOnOahu;this.childrenReadyOnMolokai=childrenReadyOnMolokai;public void run()AdultItinerary();private Condition childrenWaitOnOahu;private Condition adultWaitOnOahu;private Condition childrenReadyOnMolokai;三、测试结果三、测试结果
限制150内