简体   繁体   English

为什么我的notify方法不能正常工作?

[英]why does my notify method not work properly?

I am improving my concurrent program by pausing threads that doing the same thing to wait for one of them finished. 我正在通过暂停执行相同操作的线程来等待它们之一完成来改进我的并发程序。 However, it cannot not wake up threads properly. 但是,它不能正确唤醒线程。 Here is the code. 这是代码。

//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;
}

This method is called by many many threads. 许多线程调用此方法。 Before a thread starts calculation, it looks up the entry to see if there is another thread calculating an identical graph. 在线程开始计算之前,它会查找条目以查看是否有另一个线程在计算相同的图形。 If so, it waits. 如果是这样,它将等待。 If not, it continues to calculate. 如果没有,它将继续计算。 After it figures out the result, it notifies all the threads that is waiting on it. 在找出结果之后,它会通知所有正在等待的线程。

I use a map to pair up graph and an object. 我使用地图将图形和对象配对。 The object is the lock. 对象是锁。 Please notice that the this map can recognize two identical graphs, that is, the following code returns true. 请注意,此映射可以识别两个相同的图,即以下代码返回true。

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

Thus, the entry.get(g) should be ok to be the lock/monitor. 因此,entry.get(g)应该可以用作锁/监视器。 However, most of the threads have not been awaken, only 3-4 threads has. 但是,大多数线程尚未唤醒,只有3-4个线程唤醒了。 When the number of threads that is waiting equals to the number of threads that my computer can create, which means all the threads are waiting, this program would never terminate. 当正在等待的线程数等于我的计算机可以创建的线程数时(这意味着所有线程都在等待),该程序将永远不会终止。

Why exactly does not the entry.get(g).notifyAll() work? 为什么entry.get(g).notifyAll()不能正常工作?

Due to the fact that you have un-synchronized gaps between times that you check the map and times that you operate on the map, you have many holes in your logic where threads can proceed incorrectly. 由于在检查地图的时间与在地图上进行操作的时间之间存在不同步的间隔,因此逻辑中存在许多漏洞,线程可能无法正确处理。 you either need to synchronize outside of your map checks or use some of the special atomic methods for ConcurrentMaps. 您要么需要在地图检查之外进行同步,要么对ConcurrentMaps使用某些特殊的原子方法。

when writing concurrent code, i like to pretend there is a malicious gnome running around in the background changing things wherever possible (eg outside of synchronized blocks). 在编写并发代码时,我想假装有一个恶意的gnome在后台四处运行,并在可能的情况下进行更改(例如,在同步块之外)。 here's the first example to get you started: 这是让您入门的第一个示例:

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

you call entry.get() twice outside of a synchronization block. 您在同步块之外两次调用entry.get() therefore, the value you get could be different between those 2 calls (the evil gnome changes the map as often as possible). 因此,这两个调用之间获得的值可能会有所不同 (邪恶的侏儒会尽可能频繁地更改地图)。 in fact, it could be null when you try to synchronize on it, which will throw an exception. 实际上,当您尝试对其进行同步时,它可能为null,这将引发异常。

additionally, wait() calls should always be made in a loop while waiting for a loop condition to change (due to the possibility of spurious wakeups, or, also in your case, multiple wakeups). 另外,在等待循环条件改变时,应始终在循环中进行wait()调用(由于可能会发生虚假唤醒,或者在您的情况下也会多次唤醒)。 lastly, you should change the loop condition before you notify. 最后,您应该通知之前更改循环条件。 @AdrianShum gave a pretty good overview of how to use wait/notify correctly. @AdrianShum很好地概述了如何正确使用等待/通知。 your while loop should not be around everything, but within the synchronized block, around the wait call alone. 您的while循环不应围绕所有内容,而应位于同步块内,仅在wait调用周围。 this is not to deal with InterruptedException (a separate issue), but to deal with spurious wakeups and notifyAll calls. 这不是要处理InterruptedException(一个单独的问题),而是要处理虚假的唤醒和notifyAll调用。 when you call notifyAll all waiting threads wake up, but only one can proceed, so the rest need to go back to waiting (hence the while loop). 当您调用notifyAll 所有等待线程都将唤醒,但是只有一个线程可以继续执行,因此其余线程需要回到等待状态(因此while循环)。

in short, writing concurrent code is hard , and what you are attempting to implement is not simple. 简而言之,编写并发代码很困难 ,并且您要实现的目标并不简单。 i would recommend reading a good book first (like Josh Bloch's "Java Concurrency In Practice") before attempting to finish this code. 在尝试完成此代码之前,我建议您先阅读一本好书(例如Josh Bloch的“ Java Concurrency In Practice”)。

In fact @jtahlborn has already raised the key of problem. 实际上,@ jtahlborn已经提出了问题的关键。 I am trying to supplement by telling what's the most obvious issue here. 我试图通过告诉最明显的问题来补充这一点。

Try to get yourself understand the basics of Condition and why it can solve those race condition as normal signalling (in windows for example) 尝试使自己了解Condition的基本知识,以及为什么它可以解决那些竞赛条件作为正常的信号(例如在Windows中)

Your logic is something like this now (assume obj is referring to same object): 您的逻辑现在是这样的(假设obj指向同一个对象):

Thread 1: 线程1:

if (!hasResult) {
    synchronized(obj) {
        obj.wait();
    }
}

Thread 2: 线程2:

hasResult = false;
// do your work
synchronized(obj) {
   obj.notify();
}
hasResult= true;

You gotta know thread 1 and thread 2 is run in parallel, therefore you may have something like 您必须知道线程1和线程2是并行运行的,因此您可能会遇到类似

Thread 1                   Thread 2
                         hasResult = false
if (!hasResult)
                         do your work
                         synchronized(obj)
                         obj.notify()
                         end synchronized(obj)

synchronized(obj)
obj.wait()
end synchronized(obj)

Thread 1 is going to wait forever. 线程1将永远等待。

What you should do is 你应该做的是

Thread 1: 线程1:

synchronized(obj) {
    while (hasResult) {
        obj.wait();
    }
}

Thread 2: 线程2:

hasResult = false;
synchronized(obj) {
   // do your work
   obj.notify();
   hasResult=true;
}

That's one of the biggest hole that @jtahlborn is talking I believe (and there are other). 我相信,这是@jtahlborn所说的最大漏洞(还有其他漏洞)。 Note that setting condition and checking condition are all protected in synchronized block. 注意,设置条件和检查条件在同步块中都受到保护。 That's the main basic idea of how Condition variable is solving the race condition illustrated before. 这是条件变量如何解决前面说明的竞争条件的主要基本思想。 Get yourself understand the idea first, and redesign your piece of code with something more reasonable. 首先让您自己理解这个想法,然后使用更合理的方法重新设计您的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM