2010-04-09 50 views
2

從有效的Java第二版項目67 266-268頁:的Java同步列表僵局

後臺線程調用s.removeObserver,它試圖鎖定觀察員,但它不能獲取鎖,因爲主線程已經有鎖。一直以來,主線程都在等待後臺線程完成移除觀察者,這解釋了死鎖。

我想通過使用ThreadMXBean(Programmatic deadlock detection in java)找出主要方法中的哪個線程死鎖,但爲什麼它不返回死鎖的線程? 我使用了一個新的Thread來運行ThreadMXBean檢測。

public class ObservableSet<E> extends ForwardingSet<E> { 
    public ObservableSet(Set<E> set) { super(set); } 
    private final List<SetObserver<E>> observers = 
      new ArrayList<SetObserver<E>>(); 
    public void addObserver(SetObserver<E> observer) { 
    synchronized(observers) { 
     observers.add(observer); 
    } 
    } 
    public boolean removeObserver(SetObserver<E> observer) { 
    synchronized(observers) { 
     return observers.remove(observer); 
    } 
    } 
    private void notifyElementAdded(E element) { 
    synchronized(observers) { 
     for (SetObserver<E> observer : observers) 
     observer.added(this, element); 
     } 
    } 
    @Override 
    public boolean add(E element) { 
    boolean added = super.add(element); if (added) 
    notifyElementAdded(element); return added; 
    } 
    @Override 
    public boolean addAll(Collection<? extends E> c) { 
    boolean result = false; for (E element : c) 
    result|=add(element); //callsnotifyElementAdded 
    return result; 
    } 

    public static void main(String[] args) { 
    ObservableSet<Integer> set = 
      new ObservableSet<Integer>(new HashSet<Integer>()); 

    final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); 

    Thread t = new Thread(new Runnable() { 
     @Override 
     public void run() { 
      while(true) { 
       long [] threadIds = threadMxBean.findDeadlockedThreads(); 
       if(threadIds != null) { 
        ThreadInfo[] infos = threadMxBean.getThreadInfo(threadIds); 
        for(ThreadInfo threadInfo : infos) { 
         StackTraceElement[] stacks = threadInfo.getStackTrace(); 
         for(StackTraceElement stack : stacks) { 
          System.out.println(stack.toString()); 
         } 
        } 
       } 
       try { 
        System.out.println("Sleeping.."); 
        TimeUnit.MILLISECONDS.sleep(1000); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
     } 
    }); 
    t.start(); 


    set.addObserver(new SetObserver<Integer>() { 
     public void added(ObservableSet<Integer> s, Integer e) { 
     ExecutorService executor = Executors.newSingleThreadExecutor(); 
     final SetObserver<Integer> observer = this; try { 
     executor.submit(new Runnable() { 
      public void run() { 
       s.removeObserver(observer); 
     } }).get(); 
     } catch (ExecutionException ex) { 
      throw new AssertionError(ex.getCause()); 
     } catch (InterruptedException ex) { 
      throw new AssertionError(ex.getCause()); 
     } finally { 
      executor.shutdown(); 
     } 
     } 
    }); 

    for (int i = 0; i < 100; i++) 
     set.add(i); 
    } 
} 

public interface SetObserver<E> { 
    // Invoked when an element is added to the observable set 
    void added(ObservableSet<E> set, E element); 
} 


// ForwardingSet<E> simply wraps another Set and forwards all operations to it. 

回答

3

你有一個僵局。

但是,您沒有一個循環,這是ThreadMXBean#findDeadlockedThreads方法指定的搜索狀態。來自javadoc:

查找處於死鎖狀態的線程週期等待獲取對象監視器或可擁有的同步器。如果每個線程在嘗試獲取週期中另一個線程已經擁有的另一個鎖時擁有一個鎖,則線程會在一個等待這兩種類型的鎖的循環中發生死鎖。

在這種情況下,主線程正在等待Future的結果。而另一個線程(不包含鎖)正在等待主線程釋放它的鎖。

0

你確定會發生死鎖嗎? 嘗試了以下改變運行的程序:

1)添加當觀察者被移除的日誌信息:標記的死鎖檢測線程作爲守護程序

 executor.submit(new Runnable() { 
     public void run() { 
      s.removeObserver(observer); 
      System.out.println("Removed myself from observers") 
    } }).get(); 

2):

t.setDaemon(true); 
t.start(); 

我的猜測是死鎖可能不會發生。

+0

發生死鎖,因爲「從觀察者身上移除」絕不會被打印出來。將線程設置爲守護進程並沒有幫助。什麼促使你說僵局沒有發生? – portoalet 2010-04-10 00:09:59

+0

@portoalet我的錯!我把代碼扔到我的IDE中,運行它,但是我運行的代碼稍有不同 - 我在removeObserver調用之前有了println *,並且它被執行了 - 我錯誤地跳到了死鎖沒有發生的結論。再次抱歉! – 2010-04-10 02:03:29