多線程競爭問題分析

 public class MyStack {  
        private List<String> list = new ArrayList<String>();  
      
        public synchronized void push(String value) {  
            synchronized (this) {  
                list.add(value);  
                notify();  
            }  
        }  
      
        public synchronized String pop() throws InterruptedException {  
            synchronized (this) {  
                if (list.size() <= 0) {  
                    wait();  
                }  
                return list.remove(list.size() - 1);  
            }  
        }  
    }
問題: 這段代碼大多數情況下運行正常,但是某些情況下會出問題。什麼時候會出現什麼問題?如何修正?
case1:刪除不存在的元素

假設現在有三個線程A、B、C,其中A用於添加元素,B、C用於刪除元素。
某時刻,棧爲空,
step1、線程B運行,獲取鎖,list.size()=0,進入wait(),wait狀態下會釋放當前鎖
step2、線程A運行,獲取鎖,添加元素,執行list.add(value),此時list.size()=1,注意:在A執行notify()之前,線程C啓動,發現其他線程已經擁有對象鎖,因此進入阻塞狀態,等待鎖
step3、線程A執行notify(),試圖喚醒等待中的線程B,但是但是但是,如果此時C獲取了對象鎖,那麼將優先執行,那麼C判斷list.size()=1,直接刪除元素,然後釋放對象鎖(疑惑:1.這裏C是否可以優先獲取對象鎖,因爲B已經在wait狀態? 2.如果C能優先獲取對象鎖,那麼如何保證C結束後B能順利被喚醒?)
step4、wait狀態下的B獲取對象鎖,直接執行list.remove(list.size()-1),發生錯誤!!!
解決辦法: 使用可同步的數據結構來存放數據,比如LinkedBlockingQueue之類。由這些同步的數據結構來完成繁瑣的同步操作。

case2:虛假喚醒

虛假喚醒就是一些obj.wait()會在除了obj.notify()和obj.notifyAll()的其他情況被喚醒,而此時是不應該喚醒的。
解決的辦法是基於while來反覆判斷進入正常操作的臨界條件是否滿足: (將if換成while)
synchronized (obj) {

while (<condition does not hold>)

obj.wait();

... // Perform action appropriate to condition

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章