Task 1.4 Communicator
- 实验要求
- 实验关键代码
- 关键代码分析
- 实验测试代码
- 测试结果分析
实验要求
◆ Implement synchronous send and receive of one word messages
– using condition variables (don’t use semaphores!)
– Implement the Communicator class with operations
• void speak(int word)
• int listen()
◆ speak() atomically waits until listen() is called on the same Communicator object, and then transfers the word over to listen(). Once the transfer is made, both can return
◆ listen() waits until speak() is called, at which point the transfer is made, and both can return (listen() returns the word)
◆ This means that neither thread may return from listen() or speak() until the word transfer has been made.
◆ Your solution should work even if there are multiple speakers and listeners for the same Communicator
实验关键代码
变量声明
Lock lock;
private int speakerNum;
private int listenerNum;
private LinkedList<Integer> words;
Condition2 listener;
Condition2 speaker;
方法实现
关键代码是peak(int word)方法和listen()方法。
Void speak(int word)方法:
public void speak(int word) {
boolean preState = Machine.interrupt().disable();
lock.acquire();
words.add(word);
if(listenerNum == 0){
speakerNum++;
System.out.println("暂时没有收听者,等待收听");
speaker.sleep();
listenerNum--;
}else{
speakerNum++;
listener.wake();
listenerNum--;
}
lock.release();
Machine.interrupt().restore(preState);
Int listen()方法:
public int listen() {
boolean preState = Machine.interrupt().disable();
lock.acquire();
if(speakerNum==0){
listenerNum++;
System.out.println("暂时没有说话者,等待说话");
listener.sleep();
speakerNum--;
}else{
listenerNum++;
speaker.wake();
speakerNum--;
}
lock.release();
Machine.interrupt().restore(preState);
return words.removeLast();
}
实验测试代码
private static class Speaker implements Runnable {
private Communicator c;
Speaker(Communicator c) {
this.c = c;
}
public void run() {
for (int i = 0; i < 5; ++i) {
System.out.println("speaker speaking" + i);
c.speak(i);
//System.out.println("speaker spoken");
KThread.yield();
}
}
}
public static void SpeakTest() {
System.out.println("测试Communicator类:");
Communicator c = new Communicator();
new KThread(new Speaker(c)).setName("Speaker").fork();
for (int i = 0; i < 5; ++i) {
System.out.println("listener listening " + i);
int x = c.listen();
System.out.println("listener listened, word = " + x);
KThread.yield();
}
}
关键代码分析
思路分析
本实验编写程序并不难,重点在理解这个问题的本质:缓冲区为0的生产者消费者问题。说者说话只要有听者就交流成功,否则阻塞等待听者;听者听话只要有说者就交流成功,否则阻塞等待说者说话。
方法解释
1、Void Speak ( int word)方法
先申请锁,将说的话存放在words链表中,并将说者人数加1;然后判断是否有听者,如果没有,则说者睡眠;唤醒后要将听者人数减1;如果有听者,则将听者唤醒,然后将听者人数减1;记得最后将锁释放。
2、int listen()方法
先申请锁,将听着人数加1,然后判断是否有说者,如果没有,则听者睡眠,唤醒后要将说者人数减1;如果有,则将说者唤醒,并将说者人数减1;最后记得将锁释放,并返回words里一条信息。
关键点和难点
关键点:理解什么时候人数增加或减少。
每次说者讲话,不管有没有听者,说者人数先加一;减一操作在每次听者将说者唤醒之后;听者人数变化同理,每次听话,不管有没有说者,人数先加一,减一操作在每次说者唤醒听者之后。
难点:如何避免听者和说者在同时执行:可使用一把互斥锁。
测试结果分析
测试结果截图
测试结果分析
测试方法共使用两个线程,一个线程调用5次speak(int word)方法,另一个线程调用5次listen()方法,从而模拟了多个听者与说者的问题,从测试结果看,听者先听,发现没有说者,则听者睡眠;说者说话,唤醒听者,交流成功。