1

我有一个包含 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

来自提要的消息 来自发件人的地图中的项目数 persepctive 0

单例的新代码

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){}
}
4

3 回答 3

3

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

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

可能执行两个线程,T1并且T2

  • 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();
    
于 2012-11-12T14:11:31.653 回答
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){}
}

我仅将其作为同步示例展示,在现实生活中,您可能应该将单例实现为 Enums :在 Java 中使用 Enum 作为单例的最佳方法是什么?

于 2012-11-12T14:37:45.513 回答
0

将您的哈希映射定义为

private volatile ConcurrentHashMap<String, Integer>

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

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

于 2012-11-12T14:10:51.780 回答