簡體   English   中英

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

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

我正在通過暫停執行相同操作的線程來等待它們之一完成來改進我的並發程序。 但是,它不能正確喚醒線程。 這是代碼。

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

許多線程調用此方法。 在線程開始計算之前,它會查找條目以查看是否有另一個線程在計算相同的圖形。 如果是這樣,它將等待。 如果沒有,它將繼續計算。 在找出結果之后,它會通知所有正在等待的線程。

我使用地圖將圖形和對象配對。 對象是鎖。 請注意,此映射可以識別兩個相同的圖,即以下代碼返回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()不能正常工作?

由於在檢查地圖的時間與在地圖上進行操作的時間之間存在不同步的間隔,因此邏輯中存在許多漏洞,線程可能無法正確處理。 您要么需要在地圖檢查之外進行同步,要么對ConcurrentMaps使用某些特殊的原子方法。

在編寫並發代碼時,我想假裝有一個惡意的gnome在后台四處運行,並在可能的情況下進行更改(例如,在同步塊之外)。 這是讓您入門的第一個示例:

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

您在同步塊之外兩次調用entry.get() 因此,這兩個調用之間獲得的值可能會有所不同 (邪惡的侏儒會盡可能頻繁地更改地圖)。 實際上,當您嘗試對其進行同步時,它可能為null,這將引發異常。

另外,在等待循環條件改變時,應始終在循環中進行wait()調用(由於可能會發生虛假喚醒,或者在您的情況下也會多次喚醒)。 最后,您應該通知之前更改循環條件。 @AdrianShum很好地概述了如何正確使用等待/通知。 您的while循環不應圍繞所有內容,而應位於同步塊內,僅在wait調用周圍。 這不是要處理InterruptedException(一個單獨的問題),而是要處理虛假的喚醒和notifyAll調用。 當您調用notifyAll 所有等待線程都將喚醒,但是只有一個線程可以繼續執行,因此其余線程需要回到等待狀態(因此while循環)。

簡而言之,編寫並發代碼很困難 ,並且您要實現的目標並不簡單。 在嘗試完成此代碼之前,我建議您先閱讀一本好書(例如Josh Bloch的“ Java Concurrency In Practice”)。

實際上,@ jtahlborn已經提出了問題的關鍵。 我試圖通過告訴最明顯的問題來補充這一點。

嘗試使自己了解Condition的基本知識,以及為什么它可以解決那些競賽條件作為正常的信號(例如在Windows中)

您的邏輯現在是這樣的(假設obj指向同一個對象):

線程1:

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

線程2:

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

您必須知道線程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)

線程1將永遠等待。

你應該做的是

線程1:

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

線程2:

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

我相信,這是@jtahlborn所說的最大漏洞(還有其他漏洞)。 注意,設置條件和檢查條件在同步塊中都受到保護。 這是條件變量如何解決前面說明的競爭條件的主要基本思想。 首先讓您自己理解這個想法,然后使用更合理的方法重新設計您的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM