java - 为什么我的通知方法不能正常工作?

标签 java locking wait notify

我正在通过暂停执行相同操作的线程以等待其中一个线程完成来改进我的并发程序。但是,它无法正确唤醒线程。这是代码。

//to store graphs, if a thread finds the graph it is going to compute is in the entry, it waits, otherwise it compute then notify all other threads waiting on it.
Map<Graph, Object> entry = new ConcurrentHashMap<Graph, Object>();

public Result recursiveMethod(Graph g) {
        if (entry.get(g) != null) {//if the graph is in the entry, waits
            synchronized(entry.get(g)) {
                entry.get(g).wait();
            }
            //wakes up, and directly return the result
            return result;
        }
        synchronized(entry) {
            if (entry.get(g) == null)//if the graph is not in the entry, continue to compute
            entry.put(g,new Object());
        }
        //compute the graph recursively calls this method itself...
        calculate here...
        //wake up threads waiting on it, and remove the graph from entry
        synchronized(entry.get(g)){
            entry.get(g).notifyAll();
        }
        entry.remove(g);
        return result;
}

此方法被许多线程调用。在线程开始计算之前,它会查找条目以查看是否有另一个线程在计算相同的图。如果是这样,它就会等待。 如果没有,则继续计算。计算出结果后,它通知所有正在等待它的线程。

我使用 map 来配对图形和对象。对象是锁。 请注意,该图可以识别两个相同的图,即以下代码返回true。

Graph g = new Graph();
entry.put(g, new Object());
Graph copy = new Graph(g);
entry.get(g) == entry.get(copy) //this is true

因此,entry.get(g) 应该可以作为锁/监视器。 但是大部分线程还没有被唤醒,只有3-4个线程被唤醒。 当等待的线程数等于我的计算机可以创建的线程数时,这意味着所有线程都在等待,该程序将永远不会终止。

为什么entry.get(g).notifyAll()不起作用?

最佳答案

由于检查 map 的时间和在 map 上操作的时间之间存在不同步的间隙,因此逻辑中存在许多漏洞,导致线程可能无法正确执行。您要么需要在 map 检查之外进行同步,要么使用 ConcurrentMap 的一些特殊原子方法。

在编写并发代码时,我喜欢假装有一个恶意的侏儒在后台运行,尽可能地改变事物(例如在同步块(synchronized block)之外)。这是帮助您入门的第一个示例:

    if (entry.get(g) != null) {//if the graph is in the entry, waits
        synchronized(entry.get(g)) {

在同步块(synchronized block)之外调用 entry.get() 两次。因此,您在这两个调用之间获得的值可能不同(邪恶的侏儒尽可能频繁地更改 map )。事实上,当您尝试对其进行同步时,它可能为 null,这将引发异常。

此外,在等待循环条件更改时,应始终在循环中进行 wait() 调用(由于可能出现虚假唤醒,或者在您的情况下,多次唤醒) 。最后,您应该在通知之前更改循环条件。 @AdrianShum 很好地概述了如何正确使用等待/通知。你的 while 循环不应该围绕所有东西,而应该在同步块(synchronized block)内,单独围绕 wait 调用。这不是为了处理 InterruptedException(一个单独的问题),而是为了处理虚假唤醒和 notifyAll 调用。当你调用 notifyAll 所有个等待线程都会被唤醒,但只有一个可以继续,所以其余的需要返回等待(因此是 while 循环)。

简而言之,编写并发代码很难,而且您尝试实现的内容并不简单。我建议在尝试完成此代码之前先阅读一本好书(例如 Josh Bloch 的“Java Concurrency In Practice”)。

关于java - 为什么我的通知方法不能正常工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11680393/

相关文章:

java - Java和Python之间的套接字连接

java - Stream reduce() 要求到底包含什么?

java - 在文本区域中显示图像

安卓锁设备通知不响

javascript - For循环中如何根据索引/迭代次数动态改变间隔时间?

c - OpenCL 内核等待/延迟

javascript - 如何让 JavaScript 等到某个事件发生?

java - 从 HDFS 读取一个简单的 Avro 文件

android - NFC 服务具体何时停用?

java - Java中具有可重入锁和条件的生产者消费者场景