Страницы

Поиск по вопросам

воскресенье, 15 декабря 2019 г.

Java. Вопросы по многопоточности

#java #многопоточность #java_ee #jvm



К примеру у меня есть какой-то объект типа Object многопоточное чтение которого происходит
гораздо чаще, чем модификация и есть два варианта реализации:

a)  обычная переменная Object obj и ReadWriteLock соответственно на методы чтения
и модификации

б) volatile Object obj, на метод модификации - простой Lock, создание нового объекта
и замены ссылки obj. Метод чтения без синхронизации(не считая записи-чтения volatile
переменной)

Какой из вариантов предпочтительнее и будет работать быстрее и почему?
Если я решил делать вариант 1.б и точно знаю, что мое приложение будет запускаться
на сервере с несколькими процессорами архитектуры x86-64, то мог ли я вообще убрать
volatile и нарушив тем самым JMM, но полагаясь на протоколы когерентности кешей процессоров?
прочитает ли ядро 2-го процессора измененные данные в ядре первого и даст ли это хоть
какой-то прирост производительности? 

    


Ответы

Ответ 1



Интересный вопрос. Попробую ответить, хотя не претендую на правильность. Буду рад замечанием в комментариях. 1) Какой вариант будет быстрее? Сказать трудно, потому что все сильно зависит от usecas'а который вы используете. А именно, от количества писателей и читателей, частоты записи и чтения, потребность в "честной" синхронизации или ее критичности и пр. В общем, нужно замерять и писать тесты. Написал для этого вот такой код : Для варианта с Lock: private Object object = new Object(); private final ReentrantLock lock = new ReentrantLock(); Supplier reader = () -> { while (lock.isLocked()) { } return object; }; Function writer = number -> { lock.lock(); object = number; lock.unlock(); return object; }; Насколько я знаю, isLocked() и unlock() имеют отношение happens-before и надобности в volatile для поля object нет. Варианта с ReentrantReadWriteLock довольно простой: private Object object = new Object(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); Supplier reader = () -> { Object result; readWriteLock.readLock().lock(); result = object; readWriteLock.readLock().unlock(); return result; }; Function writer = number -> { readWriteLock.writeLock().lock(); object = number; readWriteLock.writeLock().unlock(); return object; }; Результаты получились следующие, если отношение между читателями и писателями 0.8 то вариант с обычным lock оказывается быстрее в 2.5-4 раза (полный код теста с jmh) 2) Из полученных измерений пришел к выводу, что удаление volatile не сыграет существенной роли. Лучше посмотреть в сторону каких-то альтернативных механизмов синхронизации.

Комментариев нет:

Отправить комментарий