java-多線程-一道阿里面試題分析

傳說這是阿里的一道面試題: 也傳說發這道題出來的做者去了tmail。下面是關於題目的描述: 面試

     這段代碼大多數狀況下運行正常,可是某些狀況下會出問題。何時會出現什麼問題?如何修正?可見博客 http://yueyemaitian.iteye.com/blog/1387901  數組

Java代碼   收藏代碼
  1. public class MyStack {  
  2.     private List<String> list = new ArrayList<String>();  
  3.   
  4.     public synchronized void push(String value) {  
  5.         synchronized (this) {  
  6.             list.add(value);  
  7.             notify();  
  8.         }  
  9.     }  
  10.   
  11.     public synchronized String pop() throws InterruptedException {  
  12.         synchronized (this) {  
  13.             if (list.size() <= 0) {  
  14.                 wait();  
  15.             }  
  16.             return list.remove(list.size() - 1);  
  17.         }  
  18.     }  
  19. }  

下面是關於這道題的分析:


   list.remove(list.size() - 1);這句代碼有可能引起數組下標越界
緣由:
假設其中一種情形呵!出問題的情形可能不少,但原理都差很少。下面的標號表明程序時序的前後順序。
 1,初始化時list的值爲0,而後線程1調用了pop,因而被wait了,而後釋放了鎖。
 2,線程2調用push,在notify以前有線程3調用pop(記住這時候線程1尚未被喚醒,還在wait住),此時線程3會由於等待鎖而掛起,或自旋,反正就是在等待鎖可用。
 3,而後線程2繼續往下執行,notify被執行(但這時候線程1是不會喚醒的,由於鎖還在線程2佔用),線程2退出push方法,釋放內置鎖,此時,線程1和線程3都在內置鎖等待隊列裏面。因爲synchronized是無法保證線程競爭的公平性,因此線程1和線程3均可能獲得鎖。
 4,假設線程1競爭到了鎖,不會出問題,正常去除list值,而後remove,執行完後線程3執行,一樣被wait住。
 5,假設線程3競爭到了鎖,問題來了,線程3會判斷到list的size不爲0,因而remove,因此list的size就爲0了,而後線程 3釋放鎖,這時候,線程1就獲得鎖,因而從wait中醒來,繼續執行,而後直接調用list的remove,因爲list的size=0,那麼remove(-1),越界錯誤就產生了。


   還有同窗說兩個線程都在wait處等候也會出問題,其實不會出問題的,由於是調用的notify而不是notifyAll,若是是調用notifyAll那麼也會出一樣的問題。


  至於改進:
  看到這個題目我就很納悶,爲何要用雙重鎖,好像沒有必要雙重鎖。我第一眼看到雙重鎖的時候就在想,出題者是否是在模擬一個套管死鎖,我也確實爲找這個死鎖付出了一些時間。可是這個雙重檢查都是可重入的鎖,都是對於this對象上的鎖。因此不存在套管死鎖。
  改進1,——最小代碼改動,就在remove以前再檢查list.size==0
  改進2,——去掉push和pop方法內的第二重鎖檢查,我確實沒有發現這個鎖會有什麼用,反而耗性能。 固然這裏仍是要有方案1的判斷(謝謝一樓提醒)
  改進3,——從新設計,若是是我來設計這麼一個生產者,消費者模式。我更願意用LinkedBlockingQueue,它有take方法阻塞消費者直到隊列可用。並且還有offer方法阻塞生產者直到隊列能夠插入,能夠有效的阻止OOM。

 
  這個題目出的好,難道是阿里有人犯過這個錯誤!呵呵!

  關於本題的討論若有任何紕漏,請你們及時指出呵!
相關文章
相關標籤/搜索