Hashmap更新未反映在线程之间

时间:2012-11-12 14:04:04

标签: java multithreading hashmap

我有一个包含hahsmap的单例类,hashmap初始化为类变量。此映射已正确更新,因为当我添加并打印它更改的大小时,但是,当我从不同的线程调用它时,映射始终为空。是否有可能发生这种情况的原因?

如果这有任何不同,我正在使用ConccurentHashMap。

由于

Singleton decleration:

    public class ClientRegistryDetailsSingleton {

        private static ClientRegistryDetailsSingleton instance = null;
        private ConcurrentHashMap<String, Integer> tickerToNumberRegistered = new ConcurrentHashMap<String,Integer>();


        protected ClientRegistryDetailsSingleton() {
             // Exists only to defeat instantiation.
        }

        public static ClientRegistryDetailsSingleton getInstance() {
            if(instance == null) {
               instance = new ClientRegistryDetailsSingleton();
            }
            return instance;
        }

        public void setTickerToNumberRegistered(ConcurrentHashMap<String, Integer> tickerToNumberRegistered) {
            this.tickerToNumberRegistered = tickerToNumberRegistered;
        }

        public ConcurrentHashMap<String, Integer> getTickerToNumberRegistered() {
            return tickerToNumberRegistered;
        }


    public void addToClienets(String ticker){}

    public void removeFromClients(String ticker){}
}

从另一个线程调用它:

String[] splitForTicker = message.split(",");

        ConcurrentHashMap<String, Integer> map = ClientRegistryDetailsSingleton.getInstance().getTickerToNumberRegistered();
        System.out.println("The number of items in the map from senders persepctive" + map.size());

输出:

The number of items in the map from senders persepctive 0
2012-11-12 14:29:12,495 [Process messages received] INFO  com.feed.feedReceive.ProcessFeedStreamLine - Successfully received a message from the feed
The number of items in the map from senders persepctive 0
1 :the size of the map now someone has added
2012-11-12 14:29:15,495 [Process messages received] INFO  com.feed.feedReceive.ProcessFeedStreamLine - Successfully received a
     来自Feed的

消息       来自发件人的地图中的项目数量为0

Singleton的新代码

public class ClientRegistryDetailsSingleton {

    private static ClientRegistryDetailsSingleton instance = new ClientRegistryDetailsSingleton();
    private volatile ConcurrentHashMap<String, Integer> tickerToNumberRegistered = new ConcurrentHashMap<String,Integer>();

    protected ClientRegistryDetailsSingleton() {
         // Exists only to defeat instantiation.
    }

    public static synchronized ClientRegistryDetailsSingleton getInstance() {
        return instance;
    }

    public synchronized ConcurrentHashMap<String, Integer> getTickerToNumberRegistered() {
        return tickerToNumberRegistered;
    }

    public void addToClienets(String ticker){}

    public void removeFromClients(String ticker){}
}

3 个答案:

答案 0 :(得分:3)

如果两个线程调用getInstance()并且尚未构造单例,则发布的代码中存在竞争条件,这可能导致构造单个实例的一个实例更多:

public static ClientRegistryDetailsSingleton getInstance() {
    if(instance == null) {                                   // Line 1
        instance = new ClientRegistryDetailsSingleton();     // Line 2
    }
}

可能执行两个线程T1T2

  • T1在第1行进行检查并输入if分支。
  • T1暂停,instance仍为null
  • T2在第1行进行检查并输入if分支。
  • T2构造类并分配给instance
  • T2将instance返回给来电者。
  • T2暂停。
  • T1再次启动并构建另一个实例并分配给instance

单个实例的构造必须是线程安全的。可能的解决方案是:

  • 制作getInstance()方法synchronized
  • 不要使用延迟初始化(如果可能):

    private static final ClientRegistryDetailsSingleton instance =
        new ClientRegistryDetailsSingleton();
    

答案 1 :(得分:0)

将哈希地图定义为

private volatile ConcurrentHashMap<String, Integer>

volatile关键字警告JVM,变量的状态可能随时被另一个线程更改,因此不能在本地缓存。

也可能需要将实例定义为volatile。

答案 2 :(得分:0)

DCL反模式的实施:http://en.wikipedia.org/wiki/Double_checked_locking

public class ClientRegistryDetailsSingleton {

    private static volatile ClientRegistryDetailsSingleton instance = null;

    private final ConcurrentHashMap<String, Integer> tickerToNumberRegistered = new ConcurrentHashMap<String,Integer>();


    private ClientRegistryDetailsSingleton() {
         // Exists only to defeat instantiation. 
         // please not that constructor should be private
    }

    public static ClientRegistryDetailsSingleton getInstance() {
         if (instance == null) {
               synchronized(ClientRegistryDetailsSingleton.class){
                  if(instance == null)
                    instance = new ClientRegistryDetailsSingleton();
               }
         }
         return instance;
    }

    //You should not break encapsulation and allow a link to HashMap escape
    private void setTickerToNumberRegistered(ConcurrentHashMap<String, Integer> tickerToNumberRegistered) {
        this.tickerToNumberRegistered = tickerToNumberRegistered;
    }

    //You should not break encapsulation and allow a link to HashMap escape
    private ConcurrentHashMap<String, Integer> getTickerToNumberRegistered() {
    }

     //I omitted the access to the hash map it's likely that some additional params required
     public void addToClienets(String ticker){}

     //I omitted the access to the hash map it's likely some additional params are required
     public void removeFromClients(String ticker){}
}

我只是作为一个同步示例展示了这一点,在现实生活中,您应该将您的单身作为枚举来实现:What is the best approach for using an Enum as a singleton in Java?