我正在测试Java多线程示例代码,但是在qB.start()的for循环中启动的线程被阻止,因为它正在等待qB监视器的输入。造成这种堵塞的原因是什么?
谢谢。
import java.util.*;
class QA {
public synchronized void open() throws Exception {
Thread o = new Thread() {
public void run() {
QB qB = new QB();
qB.start();
}
};
o.start();
}
public static void main(String args[]) throws Exception {
new QA().open();
}
public class QB {
private boolean shutdown;
private Vector<Thread> tList;
private final Object waitingLock = new Object();
public QB() {
tList = new Vector<Thread>();
}
public synchronized void start() {
for(int i = 0; i < 1; i++) {
final int id = i;
Thread t = new Thread("Thread " + id) {
public void run() {
load(id);
}
};
tList.add(i, t);
t.start();
}
tMonitor();
waitUntilFinished();
}
private void tMonitor() {
Thread cmt = new Thread("T Monitor Thread") {
public void run() {
synchronized(waitingLock) {
while(tList.size() > 0) {
try {
sleep(10000);
} catch(Exception e) {
e.printStackTrace();
}
}
waitingLock.notifyAll();
}
}
};
cmt.start();
}
private void waitUntilFinished() {
synchronized(waitingLock) {
while(!isShutDown()) {
try {
waitingLock.wait();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
private synchronized void load(int id) {
try {
System.out.println("blocked here");
// some work done here
removeFromTList(id);
} catch(Exception e) {
e.printStackTrace();
}
}
public synchronized boolean isShutDown() {
return shutdown;
}
}
}
答案 0 :(得分:3)
我看到的第一个问题是在QB#start()
的实例上QB
已同步。
在您尝试生成的线程t
内,load(id)
也在同一QB
实例上同步。因此,在t.start()
完成之前调用t
QB#start()
个线程块。
据推测,在QB#start()
方法结束时,QB#waitUntilFinished()
应该等待所有t
个线程完成,但它们甚至无法进入QB#load
}方法,因为他们仍在等待QB#start()
方法释放QB
实例上的锁。
所以,循环僵局。
答案 1 :(得分:1)
修改强>
好的,现在我们看到如何从tList
中删除线程,这个bug已经完全显示出来了。
如果索引0线程首先完成,那么它将从列表中删除它自己。这意味着当索引1线程完成时,它将从Vector
中删除第1个位置,但这不再指向自身。它正在删除#2线程。当删除发生时,你迟早会得到一个异常,因为它将删除一个无效的索引。
您需要从Vector
按地址而不是按位置删除项目:
tList.remove(this);
这将从列表中删除当前线程。您还应该在启动循环中执行add(t)
而不是add(i t)
:
tList.add(t);
你现在根本不需要传递给你线程的id位置。
我不知道您从tList
删除已完成的帖子的位置。我看到了removeFromTList()
方法的定义(不是您编辑了OP),但我没有看到它在任何地方使用过。在tMonitor
,你在这里有一个while循环:
while(tList.size() > 0) {
try {
sleep(10000);
} catch(Exception e) {
e.printStackTrace();
}
}
// you never get to this line
waitingLock.notifyAll();
但我没有看到任何从列表中移除线程的内容。也许当线程各自完成时,它们应该自行移除?
如果tMonitor
线程永远不会离开该循环,那么它永远不会调用:
waitingLock.notifyAll();
因此主线程将永远挂在waitUntilFinished();
。
synchronized(waitingLock) {
while(!isShutDown()) {
try {
waitingLock.wait();
} catch(Exception e) {
e.printStackTrace();
}
}
另外,您不想在sleep
中tMonitor()
进行synchronized
,因为您处于 waitingLock.wait(10000);
区块。你应该做的是:
{{1}}
没有什么能够通知它,但在睡眠中保持锁定是不好的形式。