Параллелизм для класса со статическими методами и методом инициализации - PullRequest
3 голосов
/ 20 марта 2012

Как сделать потокобезопасным для следующего класса?

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 .: Мой окончательный вариант (только внутренний класс), следующий за Джоном Винтом:

protected static class LazyLoaded
{
    static final Map<String,String> map;
    static
    {
        Map<String,String> mapInit = new HashMap<>();
                    // ...loading...
        map = Collections.unmodifiableMap(mapInit); 
    }
}

Ответы [ 5 ]

2 голосов
/ 20 марта 2012

Я бы делегировал в детский класс

public class Helper
{
    public static Map<String,String> getMap()
    {
         return HelperDelegate.map;
    }

    private static class HelperDelegate { 
           private static Map<String,String> map = new HashMap<String,String>();
           static{
                 //load some stuff
           } 

    }
}

Поскольку загрузка классов является поточно-ориентированной и происходит только один раз, это позволит вам лениво инициализировать вашу карту.

2 голосов
/ 20 марта 2012

Простая синхронизация доступа к ссылке на карту не решает проблему.После создания карты вам необходимо синхронизировать операции, которые обращаются к содержимому карты и, возможно, изменяют его.Исключением было бы, если бы вы знали, что карта заполняется один раз во время инициализации, и после этого выполняются только операции чтения.В этом случае у вас может быть все в порядке без явной синхронизации, потому что ваш поток управления позаботится об этом (зная, что логика вашей программы разрешает чтение только после инициализации карты).

В противном случае, я бы предложил вам использовать один изреализации ConcurrentMap, например ConcurrentHashMap, поскольку они относительно дешевы, если нет конфликта блокировок, и все еще обеспечивают необходимую безопасность потока, если вы выполняете чтение и запись в течение времени жизни карты.

Что касаетсяинициализация, так как это статическое поле (только один экземпляр) и стоимость создания пустой карты за один раз невелика, я бы посоветовал вам просто объявить вашу карту следующим образом:

private static final Map<String,String> map = new ConcurrentHashMap<String,String>();

Этотак как вам не нужен условный код в других методах, нет проблем с видимостью ссылок, и код становится проще.

0 голосов
/ 20 марта 2012

Во-первых, последующий доступ к карте также должен быть синхронизирован, если только новый HashMap () не будет заменен новым java.util.ConcurrentHashMap (). Затем, если после инициализации переменная map никогда не будет снова установлена ​​в null (или в любое другое значение), тогда она может быть объявлена ​​как volatile, метод getMap () останется несинхронизированным, метод ant init () будет синхронизирован. Метод init () должен еще раз проверить, является ли карта нулевой.

Кстати, в вашем исходном коде генерируется 2 HashMaps. getMap () должен возвращать карту переменных, а не новую HashMap ().

0 голосов
/ 20 марта 2012

Двойная нулевая проверка в вашем методе инициализации:

public init() {
    if (map==null) {
        synchronized(Helper.class) {
            if (map==null)
                 map = new HashMap();
        }
    }
}

Btw:

  1. HashMap не является потокобезопасным.
  2. поставить синхронизированный наМетод экземпляра не является потокобезопасным, поскольку вы обращаетесь к статической переменной, т. е. это переменная класса, а не переменная экземпляра
0 голосов
/ 20 марта 2012

Синхронизировать инициализацию и поставить еще одну проверку на ноль

public class Helper
{
    private static Map<String,String> map = null;

    public synchronized init()
    {
        if(map != null)
            return;
        map = new HashMap<String,String>();
        // some loading stuff
    }

    public getMap()
    {
       if(map==null) init();
       return new HashMap<String,String>(map);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...