2015-05-04 132 views
-1

我有以下代碼:填充單地圖

public class SomeClass{ 

    private static volatile TreeMap<String, SomeData> map; 

    public static TreeMap<String,String> getSingletonMap(){ 

     if(map=null){ 
     synchronized(SomeClass.class){ 
      if(map==null){ 
      map = new TreeMap<>(); 
      } 
     } 
     } 
    return map; 
    } 

public static synchronized void add(SomeData data){ 
    TreeMap<String,String> treeMap = getSingletonMap(); 
    treeMap.put(data.key, data); 
} 

public final class SomeData{ 
    String key; 
    public SomeData(String key){ 
     this.key = key; 
    } 

} 

}

public class SomeOtherClass{ 

    //this method is called by multiple threads 
    public static synchronized collectData(){ 
     try{ 
      //do some iteration here and add data at every iteration 
      for(String str : strings){ 
       SomeClass.add(new SomeClass.SomeData(string)); 
       } 
     } 
     finally{ 
      //do some processing of collected data by main Thread 
      // at this point TreeMap of SomeClass is null; 
     } 
    } 

} 

}

每次不同的線程訪問循環和添加數據,樹形圖再次被初始化。並且在finally塊中,當主線程繼續時,它也會被重新初始化,但是TreeMap應該是一個單例。我在這裏錯過了什麼?有任何想法嗎?

+3

請告訴我們真正的代碼,或者更好的是一個完整的小例子,重現問題。這段代碼甚至不會編譯。 –

+3

您的代碼無法編譯,因此請向我們展示您真正使用的代碼。謝謝。 – Tom

+0

我無法重現您的問題。也許你的測試結構有問題:確保'collectData()'中的'strings'不總是相同的,因爲如果它們是* equal *,新的'key'將覆蓋舊的密鑰。並且確保在「睡眠」或「等待」一段時間後檢查'map'的內容,因爲主線程不會等待直到子線程填充該映射。 – Tom

回答

0

樹映射它自身需要同步用這種方式來做到這一點

SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...)); 

,或者也可以使用Map<String, String> treeMap = new ConcurrentSkipListMap<String, String>();

+0

我使用ConcurrentHashMap進行測試,但結果保持不變。我很奇怪爲什麼TreeMap會被重新初始化爲每個線程。 – omez

+0

最有可能它是樹形圖本身的問題,因爲我注意到如果很多線程嘗試訪問樹形圖中的同一對象,它不是線程安全的,那就是爲什麼新的ConcurrentSkipListMap –

+0

看到了,如果您將代碼更改爲Hashtable,進行測試是表現相同 –

1

有很多不同的Singleton實現的。你在做什麼被稱爲"double checked locking"。在Java 5之前,這被稱爲「破碎」模式。即使是volatile關鍵字也無法修復它。但是從Java 5開始,他們fixed the volatile keyword所以你的模式是正確的。

但是,如果您使用多個虛擬機(例如與JNDI共享對象),那麼這可能是不夠的。序列化可以打破它。另一種打破它的方法是使用多個類加載器。

現在單身人士通常使用enum來實現。通過實現以下私有方法,您可以使它們支持多個VM。

private Object readResolve() { 
     return INSTANCE; 
    } 

還有其他的方法和側面的情況下,這也解釋here

+0

謝謝。在這種情況下不應該發生這樣的情況,即2個線程進入getSingletonMethod(),因爲此方法是從另一個同步方法調用的。我更深入地研究了代碼(順便提一句,這是一個集羣環境 - 也許我應該提到ealier),並且我懷疑這是多個JVM共享數據的效果,這是有意義的,因爲單例可以是單個JVM的單例並且在多個JVM的情況下需要其他類型的協調。我想這是事實。 – omez

+0

Jup就是這個原因!序列化沒有正確檢查。也許你應該讓它變成'瞬態'?或者在這裏提出一些解決方案:http://www.javaworld.com/article/2073352/core-java/simply-singleton.html?page=2 – bvdb

1

你有getSingletonMap功能錯字

if(map = null) 

應該

if(map == null) 

但是因爲你的add方法是同步所有這些懶惰的初始化是沒有意義的。您沒有獲得任何性能優勢,並且使代碼變得更加複雜。創建一個空的地圖也不是很貴:擺脫getSingletonMap方法完全的:

private static final TreeMap<String, SomeData> map = new TreeMap<>(); 

public static synchronized void add(SomeData data) { 
    map.put(data.key, data); 
} 
+1

正確的看起來就是他想要做的。或者更好的是創建ConcurrentHashMap並從add方法中刪除同步。這比靜態同步更好。 –

+0

感謝您的輸入,但我用手輸入了代碼... if(map-null)甚至不會編譯。代碼編譯並運行良好。在聲明行中初始化映射將簡單地爲每個線程清空。但正如我上面所說,這可能與集羣環境中的處理有關。單身實現本身如果完美的話。 – omez