Multithreading: sync

synchronized

synchronized void incrementSync() {     count = count + 1;}

ReentrantLock

ReentrantLock lock = new ReentrantLock();int count = 0;void increment() {     lock.lock();     try {         count++;     } finally {         lock.unlock();     }}

Позволяет проверять не занят ли мониор:

ExecutorService executor = Executors.newFixedThreadPool(2);ReentrantLock lock = new ReentrantLock();  executor.submit(() -> {     lock.lock();     try {         sleep(1);     } finally {         lock.unlock();     }});  executor.submit(() -> {     System.out.println("Locked: " + lock.isLocked());     System.out.println("Held by me: " + lock.isHeldByCurrentThread());     boolean locked = lock.tryLock();     System.out.println("Lock acquired: " + locked);});stop(executor);

Пока первый поток удерживает блокировку, второй выведет следующую информацию:

Locked: true

Held by me: false

Lock acquired: false

Метод tryLock(), в отличие от обычного lock() не останавливает текущий поток в случае, если ресурс уже занят.

ReentrantReadWriteLock

Блокировку для чтения (read-lock) может удерживать любое количество потоков до тех пор, пока не удерживает блокировка для записи (write-lock).

ExecutorService executor = Executors.newFixedThreadPool(2);Map<String, String> map = new HashMap<>();ReadWriteLock lock = new ReentrantReadWriteLock();  executor.submit(() -> {     lock.writeLock().lock();     try {         sleep(1);         map.put("foo", "bar");     } finally {         lock.writeLock().unlock();     }});
Runnable readTask = () -> {     lock.readLock().lock();     try {         System.out.println(map.get("foo"));         sleep(1);     } finally {         lock.readLock().unlock();     }};  executor.submit(readTask); executor.submit(readTask);stop(executor);

StampedLock

ExecutorService executor = Executors.newFixedThreadPool(2);StampedLock lock = new StampedLock();  executor.submit(() -> {     long stamp = lock.tryOptimisticRead();     try {         System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));         sleep(1);         System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));         sleep(2);         System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));     } finally {         lock.unlock(stamp);     }});  executor.submit(() -> {     long stamp = lock.writeLock();     try {         System.out.println("Write Lock acquired");         sleep(2);     } finally {         lock.unlock(stamp);         System.out.println("Write done");     }});stop(executor);

Оптимистичная блокировка для чтения, вызываемая с помощью метода tryOptimisticRead(), отличается тем, что она всегда будет возвращать “штамп” не блокируя текущий поток, вне зависимости от того, занят ли ресурс, к которому она обратилась. В случае, если ресурс был заблокирован блокировкой для записи, возвращённый штамп будет равняться нулю. В любой момент можно проверить, является ли блокировка валидной с помощью lock.validate(stamp). Для приведённого выше кода результат будет таким:

Optimistic Lock Valid: true

Write Lock acquired

Optimistic Lock Valid: false

Write done

Optimistic Lock Valid: false

Иногда может быть полезным преобразовать блокировку для чтения в блокировку для записи не высвобождая ресурсы.

В StampedLock это можно сделать с помощью метода tryConvertToWriteLock()

Семафоры

Семафоры — отличный способ ограничить количество потоков, которые одновременно работают над одним и тем же ресурсом:

ExecutorService executor = Executors.newFixedThreadPool(10);Semaphore semaphore = new Semaphore(5);Runnable longRunningTask = () -> {     boolean permit = false;     try {         permit = semaphore.tryAcquire(1, TimeUnit.SECONDS);         if (permit) {             System.out.println("Semaphore acquired");             sleep(5);         } else {             System.out.println("Could not acquire semaphore");         }     } catch (InterruptedException e) {         throw new IllegalStateException(e);     } finally {         if (permit) {             semaphore.release();         }     }}IntStream.range(0, 10)     .forEach(i -> executor.submit(longRunningTask));stop(executor);

В этом примере сервис-исполнитель может потенциально запустить все 10 вызываемых потоков, однако мы создали семафор, который ограничивает количество одновременно выполняемых потоков до пяти. Снова напомню, что важно освобождать ресурсы именно в блоке finally{} на случай выброса исключений. Для приведённого выше кода вывод будет следующим:

Semaphore acquired

Semaphore acquired

Semaphore acquired

Semaphore acquired

Semaphore acquired

Could not acquire semaphore

Could not acquire semaphore

Could not acquire semaphore

Could not acquire semaphore

Could not acquire semaphore

LINK to source: https://tproger.ru/translations/java8-concurrency-tutorial-2/