java - 具有静态方法和初始化方法的类的并发

标签 java concurrency static

如何使以下类成为线程安全的?

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

到目前为止我的想法:

  1. 使 getMap() 同步。 问题:使程序比必要的慢,因为同步只在程序开始时需要,然后再也不会。

  2. 使用锁。问题是我在这里使用的方法“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/

相关文章:

java - shutdown() 之后需要 ExecutorService.awaitTermination()

java - GWT Place Class 增强可行性

linux - 如果父进程仍在运行,子进程的 PID 是否可以重用?

c# - 继承和共享静态字段

java - 我该怎么做才能修复 java.lang.StringIndexOutOfBoundsException?

java - 关于 JTextFields 和输入

azure - 并发在 Azure 表存储中不起作用

javascript - JS 在 AJAX 调用返回输出之前返回?

objective-c - 共享常量定义变量的标准是什么

multithreading - 使用 `Rc` 和 `thread_local!` 在多个线程之间共享包含 `OnceCell` 引用的静态延迟初始化对象