java中的简单线程问题

时间:2010-11-03 18:33:52

标签: java multithreading concurrency synchronization

除了我释放爬虫时的工作:

public void setCrawlerFree(WebCrawler w)
    {
        synchronized(myFreeCrawlers)
        {
            synchronized(numToGo)
            {
                myFreeCrawlers.add(w);
                myFreeCrawlers.notifyAll();
                numToGo--;
                numToGo.notify();
            }
        }
    }

当抓取工具完成后,我可以将其添加回列表中。我还想从我仍然需要做的事情中减去1。我有一个主线程等待,直到numToGo为0.我在numToGo.notify()上得到一个IllegalMonitorStateException,但由于它在同步块内部,这是不是意味着我拥有它?

5 个答案:

答案 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将使用对象监视器。线程在进入同步块时获取对象的监视器。一个对象只有一个监视器,它充当互斥锁(互斥锁)。如果您在没有先获取对象监视器的情况下尝试waitnotify(未在同步块中执行waitnotify),则VM无法设置并且所以抛出IllegalMonitorException。它说“我不能允许这样做,因为,例如,如果我等待,当有人打电话通知时,我何时会知道我可以进步?他们通知什么/他们?”。它使用监视器进行协调,因此强制您获取监视器。

所以,你得到的错误是因为numToGo没有在另一个线程中同步(正如Michael先前所说)。

我不明白为什么你需要numToGo,如果它是生产者/消费者,你想在一定数量之后停止吗? bug捕获器捕获10个bug并且嬉皮士释放10个?听起来不像你要做的那样(因为他们都可能有不相关的内部计数器),所以我不确定你在那里做什么。如果我完全走错了切线,那么勾勒出你想要做的事情会很好!