Concurrent
Что это
Это пакет в java.util предназначенный для работы с многопоточность.
Основные разделы
- Concurrent Collections - набор коллекций, более эффективно работающие в многопоточной среде нежели
стандартные универсальные коллекции.
- Queues - неблокирующие и блокирующие очереди с поддержкой многопоточности. Неблокирующие очереди заточены
на скорость и работу без блокирования потоков. Блокирующие очереди используются, когда нужно «притормозить»
потоки «Producer» или «Consumer», если не выполнены какие-либо условия, например, очередь пуста или
переполнена, или же нет свободного «Consumer»'a.
- Synchronizers - вспомогательные утилиты для синхронизации потоков.
- Executors - содержит в себе отличные фрейморки для
создания пулов потоков, планирования работы асинхронных задач с получением результатов.
- Locks - представляет собой альтернативные и более гибкие механизмы
синхронизации потоков по сравнению с базовыми synchronized, wait, notify, notifyAll.
- Atomics - классы с поддержкой атомарных операций над примитивами и ссылками.
Способы синхронизация
- wait/notify
- synchronized
- Thread.join
- Lock
- Semaphore
Synchronized
synchronized - это ключевое слово, которое позволяет заблокировать доступ к методу или части кода, если его
уже использует другой поток. По принципу Mutex. Все остальные потоки которые попробуют получить монитор,
станут wait(). После выхода из монитора вызывается - notify().
Применения
- Для блока кода - если не нужно синхронизировать весь метод. Нужно передавать объект в качестве
монитора. Обычно передается this, это делает синхронизацию по текущему объекту.
private Object key = new Object();
synchronized (key) {
System.out.println("Hi I'm synchronized block!");
}
- Для метода. В качестве объекта будет текущая ссылка на объект(this).
synchronized void myMethod() {
System.out.println("Hi I'm synchronized method!");
}
Можно воспринимать так:
void myMethod() {
synchronized(this) {
System.out.println("Hi I'm synchronized method!");
}
}
Для статического метода передается ".class". По этому статическая блокировка и не статическая, на
одном классе не будут блокировать друг друга:
static void myMethod() {
synchronized(MyObject.class) {
System.out.println("Hi I'm synchronized method!");
}
}
Минус synchronized - другие потоки вынуждены ждать, пока нужный объект или метод освободится "bottle
neck". Если у объекта два синхронизированных метода, два потока не могут одновременно зайти в два
синхронизированных метода одного и того же объекта.
Volatile
volatile - ключевое слово для переменной. Указывает что переменная может быть изменена многими потоками.
- Не блокирует другие потоки.
- Не используется для инкремента.
- Она всегда будет атомарно read/write(не про атомарность измений). Даже если это 64-битные
double или long. Несколько потоков не должны менять(edit) значение.
- Java-машина не будет помещать ее в кэш потоков(кеш процессора). Так что ситуация, когда 10 потоков
работают со своими локальными копиями этой переменной - исключена.
Условия использования volatile:
- Все что будет записано в переменную не зависит от текущего значения переменной или только один поток
может изменять.
- Переменная не участвует в некоторых постоянных связях между переменными являющимися состоянием объекта
на протяжении его всего жизненного цикла.
- Если нам не нужно ограничивать некоторое действие над переменной от других потоков.
Пример
public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) {
// create and start a new thread
new Thread(() -> {
while (!flag) {
// do some work
}
System.out.println("Thread finished");
}).start();
// set the flag to true after a delay
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
}
}
Atomic
Это пакет для атомарных изменений и операций, которые выполняются независимо и безопасно для многопоточности,
без использования synchronized или Lock, что уменьшает риск Deadlock.
Часто используется как счетчик/генератор уникальных ID. Предотвращает Race conditions
Compare and Swap(CAS) - паттерн который используют Atomic классы. Часто быстрее чем синхронизация или блокировка(lock-free).
Оптимистическая блокировка. Работает на уровне CPU.
- Берется значение.
- Инкриминируется значение.
- Перед комитом изменения, снова Берется значение.
- Сравнивается с полученным в начале.
- Если оно не было - изменение комитится. Если оно было - изменение операция повторяется.
Виды:
- AtomicBoolean
- AtomicInteger
- AtomicLong
- AtomicIntegerArray
- AtomicLongArray
- AtomicReference
- AtomicReferenceArray
- AtomicStampedReference
- AtomicMarkableReference
Основные методы:
- get() - получить значение.
- set(value) - установить значение.
- getAndSet(value) - получить и сразу изменить.
- compareAndSet(expected, update) - изменить если значение равное ожидаемому.
- incrementAndGet() / decrementAndGet() - атомарное увеличение/уменьшение.
- getAndIncrement() / getAndDecrement() - атомарное увеличение/уменьшение с возращением старого значения.
- increment()
- getCounter()