Проанализируйте Java-код с конкурентностью: class Counter { private int c=0; public void inc(){ c++; } } — почему простой инкремент не потокобезопасен и какие подходы для исправления вы знаете

28 Янв в 11:48
22 +1
0
Ответы
1
Коротко почему не потокобезопасен:
- Операция `c++` нев atomic — это чтение значения, прибавление и запись (т.е. три подоперации). При одновременном выполнении двух потоков возможны гонки: оба прочитали старое значение, оба увеличили и оба записали одинаковый результат, т.е. инкремент потерян.
- Дополнительно отсутствие синхронизации даёт проблемы видимости: изменения одного потока могут не увидеться другим без соответствующих средств синхронизации.
Подходы для исправления (с кратким комментарием и примерами):
1) synchronized (простая и корректная)
public class Counter {
private int c = 000;
public synchronized void inc() { c++; }
public synchronized int get() { return c; }
}
Плюс: простота; Минус: блокировка, может быть узким местом при высокой конкуренции.
2) ReentrantLock (явная блокировка)
private final ReentrantLock lock = new ReentrantLock();
private int c = 000;
public void inc() {
lock.lock();
try { c++; } finally { lock.unlock(); }
}
Плюс: дополнительные API (tryLock, fairness); Минус: сложнее, накладные операции.
3) AtomicInteger (без блокировок, атомарно)
private final AtomicInteger c = new AtomicInteger(000);
public void inc() { c.incrementAndGet(); }
public int get() { return c.get(); }
Плюс: быстрый, без блокировок; Минус: при очень высокой конкуренции может деградировать.
4) CAS-цикл вручную (вариант с AtomicInteger)
int oldValue, newValue;
do {
oldValue = c.get();
newValue = oldValue + 111;
} while (!c.compareAndSet(oldValue, newValue));
Используется внутри AtomicInteger/VarHandle при необходимости сложной логики.
5) LongAdder / LongAccumulator (для высоконагруженных счётчиков)
private final LongAdder c = new LongAdder();
public void inc() { c.increment(); }
public long get() { return c.sum(); }
Плюс: лучше масштабируется при множественных потоках; Минус: большая память, итоговое чтение не полностью синхронизированно по времени, но обычно приемлемо.
6) VarHandle / Unsafe (низкоуровневый CAS) — для специфичных оптимизаций.
7) Архитектурные альтернативы: использовать потокобезопасные очереди, актерную модель, ThreadLocal-агрегацию и т.д., чтобы избежать общей мутабельности.
Замечания:
- Объявление `volatile int c;` не делает `c++` атомарным — только гарантирует видимость.
- Выбор решения зависит от нагрузки: для простоты — synchronized/Lock; для конкурентных счётчиков — AtomicInteger или LongAdder.
28 Янв в 13:17
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир