Абсолютно простейший пример, который я могу придумать, - сделать инкрементную атомарную операцию.
со стандартными значениями:
private volatile int counter;
public int getNextUniqueIndex() {
return counter++; // Not atomic, multiple threads could get the same result
}
С AtomicInteger:
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
Последний - очень простой способ выполнения простых эффектов мутации (особенно подсчета или уникальной индексации), без необходимости прибегать к синхронизации всего доступа.
Более сложную логику без синхронизации можно использовать, используя compareAndSet()
как тип оптимистической блокировки - получить текущее значение, вычислить результат на его основе, установить этот результат тогда значение по-прежнему является входным используется для выполнения вычислений, иначе начнем снова - но примеры подсчета очень полезны, и я буду часто использовать AtomicIntegers
для подсчета и генераторов уникальных для всей VM, если есть какие-либо намеки на участие нескольких потоков, потому что они так с ним легко работать Я бы даже посчитал преждевременной оптимизацию использовать простой ints
.
Хотя с помощью ints
и соответствующих объявлений synchronized
почти всегда можно добиться одинаковых гарантий синхронизации, прелесть AtomicInteger
в том, что безопасность потока встроена в сам реальный объект, а не о том, что вам нужно беспокоиться о возможных чередованиях и контролируемых мониторах каждого метода, который получает доступ к значению int
. Гораздо сложнее случайно нарушить безопасность потоков при вызове getAndIncrement()
, чем при возврате i++
и запоминании (или нет) для получения правильного набора мониторов заранее.