Сам не первый год пишу на java, но лишь в рамках хобби, с многопоточностью приходится не так часто работать.
Заинтересовали несколько вопросов, а имённо:
Допустим, мне нужно писать и вычитывать примитивный тип на каком-то объекте (из разных потоков). Допустим, поля открытые (public
).
Если верить справке java, операции чтения и записи ссылок являются атомарными (логично, ведь размер такой ссылки обычно помещается в регистр процессора, да и система его поддерживает). Тогда чтение и запись поля, хранящего ссылку на объект, вполне себе нормальная операция для выполнения в разных потоках.
Также справка java уверяет, что чтение и запись примитивных типов, исключая long
и double
, тоже являются атомарными. Но как быть с этими двумя? Как я понимаю, их нужно лишь пометить как volatile
. Но есть ли какие-то побочные эффекты, или этот модификатор только и делает, что гарантирует атомарность операций над переменной?
Хорошо. А теперь, к примеру, я хочу читать и писать поля через геттеры-сеттеры. Если мои геттеры-сеттеры не меняют состояние каких-либо внешних данных, а лишь читают и пишут переменную, достаточно ли мне пометить такую переменную как volatile
, или же нужно отмечать геттер/сеттер как syncronized
? (При условии, что доступ к переменным возможен лишь через геттер и сеттер).
Работа с объектами. А конкретно, с объектами, что не меняют состояния.
К примеру, String
. Насколько мне известно, данный тип никогда не меняет состояния, а все его методы, возвращающие строку, возвращают новый экземпляр.
Так вот, могу ли я обращаться к такому объекту из разных потоков без дополнительной синхронизации? Или же я могу тогда что-то упустить (вопрос для случая, когда я могу лишь получить объект, но не изменить значение поля, на него ссылающегося).
Благодарю за терпение того, кто сможет на эти вопросы ответить)
Важно, что volatile
гарантирует видимость. Изменения volatile-поля в одном потоке будут сразу видны в другом. Если вам нужно потокобезопасно выполнить более сложную процедуру, чем присвоение - воспользуйтесь synchronized или j.u.c.Atomic*
-типами, чтобы избежать возникновения гонок (race condition).
Пример: любой многопоточный счетчик
// Плохо
private volatile long counter;
public long getId() {
counter += 1;
return counter;
}
// Хорошо
private volatile AtomicLong counter = new AtomicLong(0);
public long getId() {
return counter.incrementAndGet();
}
// Тоже хорошо
private volatile long counter;
public synchronized long getId() {
counter += 1;
return counter;
}
Помните, что synchronized
- это всегда явная блокировка (но она довольно дёшева при низкой конкуренции), а Atomic-примитивы используют бесконечный цикл с CAS-инструкциями и могут хорошо оптимизироваться.
Объекты с неизменяемым состоянием (например, все поля такого объекта final
) назвают immutable объектами, и с ними безопасно работать из разных потоков. Но если внутреннее состояние объекта содержит изменяемый какой-то компонент и он как-то может быть доступен снаружи - придется дополнительно думать о потокобезопасности. Пример:
// потокобезопасный класс
public final class SafeFoo {
private final Date date = new Date();
}
// не потокобезопасный класс
public final class UnsafeFoo {
private final Date date = new Date();
public Date getDate() { return date; }
}
Первый класс полностью потокобезопасен. Второй - нет. Метод getDate()
осуществляет публикацию внутреннего состояния внешнему коду (unsafe publication). И где-то снаружи можно сделать так, изменив внутренне состояние:
unsafeFoo.getDate().setHours(0);
Защититься от этого можно, например созданием копии:
// потокобезопасный класс
public final class SafeAgainFoo {
private final Date date = new Date();
public Date getDate() { return date.clone(); }
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Есть ListView с множественным выбором,в 1 элемент которого входит к примеру - ингридиент для рецептовКак мне реализовать поиск по моей бд в firebase...
Я хочу получить последнюю дату входа пользователя в базу данных Firebase для AndroidИнформация нужна для удаления неактивных пользователей из базы...