一个Java同步方法的入口点是否足够线程安全?

3
我有一个Singleton类处理一种具有不同对象的Hashmap缓存。 (键的格式直接与存储在映射中的对象类型相关 - 因此映射是of​​)
地图上可以执行三个不同的操作:添加、获取、删除。
我通过使用公共入口方法来保护对地图的访问(没有强烈的访问):
public synchronized Object doAction(String actionType, String key, Object data){
  Object myObj = null;
  if (actionType.equalsIgnorecase("ADD"){
    addDataToMyMap(key,data);
  } else if (actionType.equalsIgnorecase("GET"){
    myObj = getDataFromMyMap(key);
  } else if (actionType.equalsIgnorecase("REM"){  
    removeDataFromMyMap(key);      
  }
  return myObj;
}

注:

该地图是私有的。方法addDataToMyMap(),getDataFromMyMap()和removeDataFromMyMap()也是私有的。只有入口方法和类本身的静态getInstance()是公共的。

如果只能通过该方法使用地图,您确认它对于并发访问是线程安全的吗?

如果对于Map是安全的,我想这个原则可以应用到任何其他共享资源上。

非常感谢您提前的回答。

David


我猜你的意思是doAction方法需要同步吗?(在你的代码片段中没有看到) - Will
你的3个方法:addDataIn...等,是否有使用synchronized关键字? - nhahtdh
其他方法是私有的,它们本身不直接同步。 - David SCHERRER
1
这意味着您的线程一次只能添加、获取或删除一个线程,这并不是最好的解决方案。 - memo
我认为将缓存方法设为非同步的,同时将入口点设置为同步的并不是一个好主意,因为这样只能执行一个操作。 - amicngh
正如我所概述的那样。虽然地图的用途不是很大,但它必须作为共享资源保持安全。将来我可能会改用同步的Map实现。 - David SCHERRER
8个回答

1

使用同步版本的映射可能仍然不足够。请查看我的答案。 - Matt

1

只要唯一的入口点是doAction,您的类就是线程安全的。


抱歉,我没有注意到同步关键字。 - Mohan
更具体地说,即使将synchronized关键字从doAction()移动到像removeDataFromMyMap()这样的特定方法中,也不足以满足要求。上述方法会给您带来一些小的性能损失(微秒级别),因为您正在公共方法级别进行同步,但它将保证线程安全。我曾经对ConcurrentHashMap与synchronized{hashmap}的性能进行过基准测试。当第70个百分位数达到15微秒时,ConcurrentHashMap比像您所做的那样同步hashmap时更快,尤其是在多个线程访问它时。 - Mohan
谢谢,那就是重点。我只是加了一条注释 - 是的,这是唯一的入口点。 - David SCHERRER

1
如果您的缓存类具有私有的HashMap,并且您有三种方法,所有这些方法都是公共同步的,而且不是静态的,如果您没有任何其他公共实例变量,那么我认为您的缓存是线程安全的。
最好发布您的代码。

1

这是完全安全的。只要所有线程都使用一个公共锁来访问它,而在这种情况下该锁是对象,那么它就是线程安全的。(其他答案可能更高效,但您的实现是安全的。)


0
这取决于你的代码。正如其他人所说,你可以使用Collections.synchronizedMap。然而,这只会同步映射上的单个方法调用。所以如果:
map.get(key);
map.put(key,value);

如果在两个不同的线程中同时执行,一个线程会阻塞直到另一个退出。但是,如果你的临界区域比调用map函数时更大:

SomeExpensiveObject value = map.get(key);
if (value == null) {
   value = new SomeExpensiveObject();
   map.put(key,value);
}

现在假设该键不存在。第一个线程执行并返回null值。调度程序暂停该线程,并运行线程2,线程2也返回null值。它构造新对象并将其放入映射中。然后线程1恢复并执行相同的操作,因为它仍然具有null值。
这就是您需要在关键部分周围使用更大的同步块的地方。
SomeExpensiveObject value = null;

synchronized (map) {
  value = map.get(key);
  if (value == null) {
     value = new SomeExpensiveObject();
     map.put(key,value);
  }
}

0

0

目前很难确定代码是否是线程安全的。你的示例缺少重要信息:

  1. 方法是否为公共方法
  2. 方法是否同步
  3. 地图是否仅通过方法访问

我建议你研究同步以了解问题及其解决方法。探索ConcurrentHashMap类将进一步了解有关您的问题的信息。


0

你应该使用ConcurrentHashMap。它比synchronized doAction提供更好的吞吐量,比Collections.synchronizedMap()提供更好的线程安全性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接