Встречал много статей, где предлагают как вполне возможный вариант использовать java.util.HashMap из разных потоков, при этом применив синхронизацию через synchronized или обернув через Collections.synchronizedMap(...).
Данная разновидность Map не имеет volative полей, следовательно, я не понимаю, в чем смысл использовать это из разных потоков через synchronized, если потоки не узнают о изменении Map, т.к. ее поля не volatile.
Пожалуйста, помогите прояснить момент!
Тема не простая, хотя казалось бы, давайте разберемся как это работает:
При указании volatile
мы говорим что нужно записать значение сразу в кучу минуя локальный кэш потока. При этом заставляя JVM отказаться от оптимизации.
Вот пример как volatile
может влиять на поведение потока (допустим у нас есть переменная int count
):
count
на 9, сейчас сделаем.volatile
нет, меняем значение в локальном кэше, поменяли.Если бы у нас было volatile int count
, то поток №1 успел бы записать значение за одну операцию сразу в кучу. И тогда поток №2 прочитал бы уже измененное значение потоком №1.
Нужно отметить что, мы не выбираем когда и какой поток будет выполняться. Если JVM считает нужным отдать приоритет на выполнение другому потоку, она это сделает. Может конечно и получиться так что без volatile
поток № 1 успеет записать значение, а может и нет. Для избежания неоднозначности можно использовать volatile
.
Это все справедливо для примитивов. Для объектов volatile
записывает сразу в кучу только ссылку. Допустим есть класс User
и мы с ним работаем, получается что теперь и поля в User
нужно пометить volatile
, тогда рекурсивно и все поля объекты User
должны быть volatile
. Теряем всю оптимизацию...
Что же делать? Тут на помощь приходит synchronized
.
synchronized
блок дает доступ только одному потоку, второй поток блокируется и ждет выхода первого.
Чем HashMap
отличается от synchronizedMap
?
@Override
public V put(K key, V value) {
synchronized (monitor) {
return provider.put(key, value);
}
}
@Override
public V get(Object key) {
synchronized (monitor) {
return provider.get(key);
}
}
В методы get()
и put()
добавлены блоки synchronized
, как можно заметить синхронизация на одном объекте. Это значит что пока первый поток не выйдет из метода get()
/put()
, второй поток будет заблокирован и ждать своей очереди. Как только первый поток выйдет из метода get()
/put()
, тогда второй поток разблокируется и сможет войти в метод get()
/put()
.
Какой вывод? Первый поток выполняет нужные ему действия получает или вставляет значение, затем приходит второй поток и выполняет свои действия с уже измененными данными (разумеется если выполнялся метод put()
).
У synchronizedMap
есть проблема, он блокирует доступ сразу и на чтение и на вставку одновременно, что снижает скорость.
Поэтому есть класс ConcurrentHashMap
. У него чтение без блокировки, а вот вставлять можно только по очереди, что делает его быстрее synchronizedMap
.
Какой вывод из этого всего: Зачем писать самому, если можно использовать уже готовые оптимизированные решения синхронизированных коллекций.
надеюсь я понятно объяснил)
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
подскажите либу чтобы можно было сделать кастомный скролл для ie11, вид скроллбара снизу
Я хочу сделать множественный фон (вода и рыба)Рыбе хочу задать определенное местоположение, например background-position: right center