如何使以下类成为线程安全的?
public class Helper
{
private static Map<String,String> map = null;
public static init()
{
map = new HashMap<String,String>();
// some loading stuff
}
public static getMap()
{
if(map==null) init();
return new HashMap<String,String>(map);
}
}
到目前为止我的想法:
使 getMap() 同步。 问题:使程序比必要的慢,因为同步只在程序开始时需要,然后再也不会。
使用锁。问题是我在这里使用的方法“isLocked”不存在。那么最好的解决方案是什么?
public class Helper { private static Map<String,String> map = null; private static Lock lock = new ReentrantLock(); public static init() { lock.lock(); map = new HashMap<String,String>(); // some loading stuff lock.unlock(); } public static getMap() { synchronized {if(map==null) init();} while(lock.isLocked()) {Thread.wait(1);] return new HashMap<String,String>(map); }
P.S.:抱歉第二个源码显示问题。在枚举之后使用代码似乎存在错误。
P.P.S.:我知道 HashMap 不是线程安全的。但这仅意味着我不能并行写入,读取应该不是问题,不是吗?
P.P.P.S.:我的最终版本(只是内部类),遵循 John Vint:
protected static class LazyLoaded
{
static final Map<String,String> map;
static
{
Map<String,String> mapInit = new HashMap<>();
// ...loading...
map = Collections.unmodifiableMap(mapInit);
}
}
最佳答案
简单地同步对 map 引用的访问并不能解决问题。创建 map 后,您需要同步访问和可能修改 map 内容的操作。如果您知道映射在初始化期间被填充了一次,并且之后只执行读取操作,则会出现异常(exception)情况。在那种情况下,您可能不需要显式同步就可以,因为您的控制流会处理这一点(知道您的程序逻辑只允许在 map 初始化后读取)。
否则,我建议您使用 ConcurrentMap
实现之一,例如 ConcurrentHashMap
,因为如果没有锁争用并且仍然提供必要的线程,它们相对便宜如果您确实在 map 的生命周期内执行读取和写入,则安全。
至于初始化,因为它是一个静态字段(所以只有一个实例)并且一次创建一个空映射的成本并不高,我建议您只需像这样声明您的映射:
private static final Map<String,String> map = new ConcurrentHashMap<String,String>();
这样您就不需要其他方法中的条件代码,不存在引用可见性问题并且代码变得更简单。
关于java - 具有静态方法和初始化方法的类的并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9789247/