📅  最后修改于: 2023-12-03 15:42:11.576000             🧑  作者: Mango
题目链接:https://gateoverflow.in/463/gate2012-57
本题属于GATE 2012计算机科学考试中的第57章,考查了对并发编程中的同步和互斥机制的理解和应用。该题目需要程序员理解和分析多线程程序,并找出其中的错误并进行修复。
下面是一个 Java 类的代码片段,它实现了一个2阶段交换协议。模拟了两个进程A和B之间的并发执行。请在保持协议的完整性不变的前提下修复错误。
import java.util.concurrent.locks.*;
class Exchange {
Object item = null;
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (item != null)
condition.await();
item = x;
System.out.println("A puts " + x);
condition.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (item == null)
condition.await();
Object x = item;
item = null;
System.out.println("B takes " + x);
condition.signal();
return x;
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
Exchange ex;
Producer(Exchange e) {
ex = e;
}
public void run() {
for (int i = 0; i < 10; i++) {
try {
ex.put(i);
} catch (InterruptedException e) {
}
}
}
}
class Consumer implements Runnable {
Exchange ex;
Consumer(Exchange e) {
ex = e;
}
public void run() {
for (int i = 0; i < 10; i++) {
try {
Object item = ex.take();
} catch (InterruptedException e) {
}
}
}
}
public class Main {
public static void main(String[] args) {
Exchange ex = new Exchange();
(new Thread(new Producer(ex))).start();
(new Thread(new Consumer(ex))).start();
}
}
答案将在下文中给出,首先我们来分析一下代码的实现逻辑。
由题目描述来看,取数和放数的交替方法看起来是正确的,并且在数据交换之前检查item的值是否为null也符合要求。因此这似乎没有什么问题。但是,如果你运行这段代码会发现它会进入死锁状态。这是为什么呢?
首先,在put方法的while循环中防止了过早或过晚的唤醒线程。如果其他线程在执行时错过了signal的调用,就会一直等待,这是一个很好的做法。接着,我们来看一下问题出在哪里。
在take方法中,被放置的值存在item变量中。当该值被从这个变量中移除时,它被设置为null。但是,在之后的signal调用之前,上面的if语句被执行。在執行condition.await()時,线程被阻止,无法在之后的signal调用中接收这个信号(即,线程不是在condition.await语句被执行后再排队等待signal,而是在之前)。
当没有其他可运行的线程时,代码会进入死锁状态。因此,必须将它放到循环中,直到新的值实际被放置才释放锁并离开循环。
因此,问题的解决方法就是将条件变量在等待之前放入while循环中。修正的代码如下所示:
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (item != null) {
condition.await();
}
item = x;
System.out.println("A puts " + x);
condition.signalAll();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (item == null) {
condition.await();
}
Object x = item;
item = null;
System.out.println("B takes " + x);
condition.signalAll();
return x;
} finally {
lock.unlock();
}
}