2015-08-25 2 views
0

У меня есть статическая HashMap, которая будет кэшировать объекты, идентифицируемые уникальными целыми числами; он будет доступен из нескольких потоков. У меня будет несколько экземпляров типа HashmapUser, работающих в разных потоках, каждый из которых захочет использовать один и тот же HashMap (поэтому он статический).Этот поток использования HashMap безопасен?

Как правило, HashmapUsers будет извлекаться из HashMap. Хотя, если он пуст, его необходимо заполнить из базы данных. Кроме того, в некоторых случаях HashMap будет очищен, так как он нуждается в изменении данных, и его необходимо повторно заселить.

Итак, я просто делаю все взаимодействия с картой synchhonized. Но я не уверен, что это безопасно, умно или работает для статической переменной.

Является ли нижеследующая реализация этой темы безопасной? Любые предложения по упрощению или иным улучшениям?

public class HashmapUser { 

    private static HashMap<Integer, AType> theMap = new HashSet<>(); 

    public HashmapUser() { 
    //.... 
    } 

    public void performTask(boolean needsRefresh, Integer id) { 
    //.... 

    AType x = getAtype(needsRefresh, id); 

    //.... 
    } 

    private synchronized AType getAtype(boolean needsRefresh, Integer id) { 
    if (needsRefresh) { 
     theMap.clear(); 
    } 

    if (theMap.size() == 0) { 
     // populate the set 
    } 

    return theMap.get(id); 
    } 
} 
+3

Любая конкретная причина, по которой вы не используете 'ConcurrentHashMap', потокобезопасную версию' HashMap'? – Powerlord

+1

хорошо, мои проблемы параллелизма возникают из таких случаев, как: несколько потоков считают, что hashmap пуст и пытается заполнить его в одно и то же время, один поток считает, что hashmap заполнен, но к тому времени, когда он пытается извлечь из него другой поток, он очистил его , ConcurrentHashMap не решает эти проблемы. (Я думаю?) – ab11

+1

Ваш синхронизированный бесполезен, так как у вас много экземпляров HashmapUser. Thread A получит блокировку пользователя1, а поток B получает блокировку на пользователе2, и оба одновременно получат доступ к карте. Вам нужен один объект, который будет использоваться всеми пользователями, а chich - единственный доступ к карте. И методы этого объекта - те, которые должны быть синхронизированы. –

ответ

3

Как бы то ни было, он определенно не является потокобезопасным. В каждом экземпляре HashmapUsers будет использоваться другой замок(), который ничего не делает. Вам нужно синхронизировать один и тот же объект, например, сам HashMap.

Изменить getAtype на:

private AType getAtype(boolean needsRefresh, Integer id) { 
    synchronized(theMap) { 
     if (needsRefresh) { 
      theMap.clear(); 
     } 

     if (theMap.size() == 0) { 
      // populate the set 
     } 

     return theMap.get(id); 
    } 
    } 

Edit: Обратите внимание, что вы можете синхронизировать любой объект, при условии, что все экземпляры используют один и тот же объект для синхронизации. Вы можете синхронизировать по HashmapUsers.class, что также позволяет другим объектам блокировать доступ к карте (хотя обычно рекомендуется использовать закрытый замок).

Из-за этого просто делается ваш статический метод getAtype, так как подразумеваемая блокировка теперь будет HashMapUsers.class вместо this. Однако это раскрывает вашу блокировку, которая может быть или не быть тем, что вы хотите.

+0

Все еще бесполезно, если есть несколько экземпляров HashmapUser –

+3

@ Daniel Как так? Блокировка - это сама HashMap, поэтому только один экземпляр может одновременно войти в «синхронизированную» область. – Meindratheal

+0

Согласен, это выглядит правильно для меня. – dimo414

0

Я хотел бы предложить вам пойти либо ConcurrentHashMap или SynchronizedMap. Дополнительная информация здесь: http://crunchify.com/hashmap-vs-concurrenthashmap-vs-synchronizedmap-how-a-hashmap-can-be-synchronized-in-java/

ConcurrentHashMap больше подходит для сценариев с высокой степенью параллелизма. Эта реализация не синхронизирует весь объект, а оптимизирует его, поэтому разные потоки, обращаясь к разным клавишам, могут делать это одновременно.

SynchronizerMap Проще и выполняет синхронизацию на уровне объекта - доступ к экземпляру является серийным.

Я думаю, что вам нужно выступление, поэтому я думаю, вам стоит пойти с ConcurrentHashMap.

+0

'SynchronizerMap' здесь не работает. Хотя он гарантирует, что сама карта не будет повреждена при одновременном доступе, она не гарантирует инвариантов между отдельными доступом. Например, для нескольких потоков можно увидеть 'theMap.size() == 0', а затем заполнить его. – dimo414

+0

Ни 'ConcurrentHashMap', ни' SynchronizedMap' - это волшебная палочка, которая сделает поток программ безопасным. Посмотрите на класс OPS 'HashmapUser': синхронизированные методы выполняют не одну операцию, а одну. 'GetAType (...)' выглядит так, как будто ему нужно синхронизировать _sequence_ операций. Невозможно рассказать о других методах: это зависит от того, что означает '...'. –

1

Нет, это не сработает.

Если вы не указали объект блокировки, например. объявить метод synchronized, неявной блокировкой будет экземпляр. Если этот метод не является статическим, блокировка будет классом. Поскольку существует несколько экземпляров, есть также несколько блокировок, которые я сомневаюсь.

Что вам нужно сделать, это создать еще один класс, который является единственным классом с доступом к HashMap.

Клиенты HashMap, такие как HashMapUser, даже не должны знать о наличии синхронизации. Вместо этого безопасность потоков должна быть гарантирована надлежащей классовой упаковкой HashMap, скрывающей синхронизацию от клиентов.

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

+0

В чем смысл добавления дополнительного класса? Класс 'HashmapUser' уже _is_ единственный класс, который знает о' theMap'. –

+0

Единый принцип ответственности. Не зная полной реализации, трудно сказать, сколько и что нарушено. То, как я вижу это, должен быть Cache (Hash Map) и Пользователи. Может быть, просто именование немного, но HashMapUser подразумевает логику и синхронизацию кеш-базы данных в одном классе. – John

Смежные вопросы