填充单例映射

时间:2015-05-04 18:58:46

标签: java multithreading

我有以下代码:

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

}

}

每次不同的线程访问循环并添加数据时,TreeMap都会再次初始化。在finally块中,当主线程继续时,它也会再次初始化,但TreeMap应该是一个单例。我在这里错过了什么?有任何想法吗?

3 个答案:

答案 0 :(得分:1)

There are a lot of different Singleton implementations. What you are doing is known as "double checked locking". Before Java 5 this was known as a "broken" pattern. Even a volatile keyword couldn't fix it. But as of Java 5 they fixed the volatile keyword. So your pattern is correct.

However if you use multiple VMs (for example sharing objects with JNDI) then this could be insufficient. The serialization can break it. Another way to break it, is the use of multiple classloaders.

These days singletons are often implemented using enum's. You can make them support multiple VMs by implementing the following private method.

  private Object readResolve() {
        return INSTANCE;
  }

There are other ways and side cases, which are also explained here.

答案 1 :(得分: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 );
}

答案 2 :(得分:0)

树图自己需要同步使用这种方式来做到这一点

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

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