除了我释放爬虫时的工作:
public void setCrawlerFree(WebCrawler w)
{
synchronized(myFreeCrawlers)
{
synchronized(numToGo)
{
myFreeCrawlers.add(w);
myFreeCrawlers.notifyAll();
numToGo--;
numToGo.notify();
}
}
}
当抓取工具完成后,我可以将其添加回列表中。我还想从我仍然需要做的事情中减去1。我有一个主线程等待,直到numToGo为0.我在numToGo.notify()上得到一个IllegalMonitorStateException,但由于它在同步块内部,这是不是意味着我拥有它?
答案 0 :(得分:7)
考虑将其重写为ExecutorService。
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize, keepAliveTime, timeUnit,
new LinkedBlockingQueue<Runnable>());
executor.submit(new Callable<...>() { ... });
它将大大简化您的代码并消除线程同步问题。
答案 1 :(得分:3)
所以我想我需要打电话给等待 在对象上通知所有的 线程有共同点,但事实并非如此 正确的。
是的,确实如此。但是:
public class IllegalMonitorStateException extends RuntimeException
抛出表示线程有 试图等待一个物体 监视或通知其他线程 等待对象的监视器没有 拥有指定的监视器。
您需要在对象上调用wait()
或notify()
之前对其进行同步。
答案 2 :(得分:0)
你的numToGo字段是一个被包装的原始类型吗? (int to Integer,long to Long等)。请记住,这些包装器是不可变的,每次装箱/取消装箱时都会导致您拥有不同的对象。始终建议在需要同步时使用最终对象。
而不是使用和整数创建自己的对象来维护值和同步。
class Counter {
private int value ;
private final Object lock = new Object() ;
public ExecutionStatus() { }
public void increment() {
synchronized(lock) {
value ++ ;
}
}
public void decrease() {
synchronized(lock) {
value-- ;
}
}
// Read dirty
public int count() {
return value ;
}
public int safeCount() {
synchronize(lock) {
return count() ;
}
}
}
从不如此,您可以将行private final Object lock = new Object()
添加到当前代码中,并使用它来控制numToGo变量的同步。
希望这有帮助。
答案 3 :(得分:0)
您正在同步非最终成员变量。您的同步(numToGo)同步numToGo的某个值,然后您更改引用:numToGo--。您现在有一个不同的实例,您调用notify,因此例外。
答案 4 :(得分:0)
那里有一些好的帖子,有很多选择,但我想这是一种学术练习?正如人们所指出的那样,当有更多现代替代方案使事情变得更容易时,你可能不会使用wait / notify / notifyAll。等待/通知的事情虽然很有趣,但作为并发工作的基础非常值得理解。
我认为这是某种消费者/生产者的事情?一个线程正在捕获一个爬虫,另一个线程将它释放?如果是这种情况,您可能希望在设置空闲之前等待陷阱占用者?它可能看起来像这样...
private final List<Object> trap = new ArrayList<Object>();
public class BugCatcher {
public void trapCrawler(Object crawler) {
synchronized (trap) {
trap.add(crawler);
System.out.println("caught bug number " + trap.size() + "!");
trap.notifyAll();
}
}
}
public class Hippy {
public void setCrawlerFree(Object crawler) throws InterruptedException {
synchronized (trap) {
trap.wait();
trap.clear();
System.out.println("set bugs free! time to hug a tree");
}
}
}
如果BugCatcher
可以比嬉皮士释放它们更快地捕获错误,那么在尝试释放错误(因此wait
调用)之前,嬉皮士会等待陷阱中包含某些内容。
如果你省略了wait / notify部分,那么事情将仅仅依赖于synchronized
关键字,一次只有一个线程会访问陷阱,并且它首先会到达那里(嬉皮士可能会尝试一个空的已经空的陷阱)。
为了协调等待和通知,VM将使用对象监视器。线程在进入同步块时获取对象的监视器。一个对象只有一个监视器,它充当互斥锁(互斥锁)。如果您在没有先获取对象监视器的情况下尝试wait
或notify
(未在同步块中执行wait
或notify
),则VM无法设置并且所以抛出IllegalMonitorException
。它说“我不能允许这样做,因为,例如,如果我等待,当有人打电话通知时,我何时会知道我可以进步?他们通知什么/他们?”。它使用监视器进行协调,因此强制您获取监视器。
所以,你得到的错误是因为numToGo
没有在另一个线程中同步(正如Michael先前所说)。
我不明白为什么你需要numToGo
,如果它是生产者/消费者,你想在一定数量之后停止吗? bug捕获器捕获10个bug并且嬉皮士释放10个?听起来不像你要做的那样(因为他们都可能有不相关的内部计数器),所以我不确定你在那里做什么。如果我完全走错了切线,那么勾勒出你想要做的事情会很好!