Вы находитесь на странице: 1из 23

Concurrency

Часть 1a

Борисов Андрей
andriy.borysov@ukr.net
Контрольная работа
1. Создайте экземпляр класса FileOutputStream, запишите в него 1 байт и
закройте.
2. Когда можно использовать volatile?

2
Concurrency. Риски
1. Большое количество задач негативно влияет на общую производительность
системы
2. При большом количестве задач ресурсы тратятся на управление задачами
вместо их выполнения, понижая общий КПД системы
3. Неизбежно состояние гонки (“race condition”) между задачами, что может
привести к повреждению данных
4. Неизбежно противоречивое состояние данных при использовании кэша
(“cache”) из-за медлительности памяти ОЗУ

3
Concurrency. Atomics
Атомарные операции подразумевают неделимые действия с точки зрения
многопоточного и одновременного выполнения задач.

Помогает решить риски:


● состояние гонки
● противоречивость данных из-за несогласованности кэша и оперативной
памяти

4
Concurrency. Atomics
Встроенные классы для выполнения простейших операций атомарным способом:
● java.util.concurrent.atomic.AtomicBoolean
● java.util.concurrent.atomic.AtomicInteger
● java.util.concurrent.atomic.AtomicIntegerArray
● java.util.concurrent.atomic.AtomicLong
● java.util.concurrent.atomic.AtomicReference<V>

5
Concurrency. Volatile
Ключевое слово volatile применяется для полей класса, чтобы гарантировать
актуальное значение этого поля среди потоков.

Помогает решить риски:


● противоречивость данных из-за несогласованности кэша и оперативной
памяти

6
Concurrency. Volatile. Пример
public class Operation {
private volatile boolean done;

public void setDone() {


done = true;
}
public boolean isDone() {
return done;
}
}

7
Concurrency. Final
Дополнительной особенностью ключевого слова final является его применение
для полей класса и переменных, чтобы гарантировать актуальное значение этого
поля среди потоков и гарантировать неизменность этого значение со временем.
Такая особенность относится к понятию “неизменяемых” (“immutable”) объектов.
Менее актуально в последних Java.

Помогает решить риски:


● состояние гонки
● противоречивость данных из-за несогласованности кэша и оперативной
памяти
8
Concurrency. Final. Пример
public class Color {
private final int alpha, red, green, blue;

public Color(int alpha, int red, int green, int blue) {


// инициализация полей
}

public int alpha() {


return alpha;
}
// ... другие getter-методы
}
9
CountDownLatch. Done signal #1
CountDownLatch doneSignal = new CountDownLatch(2000);
Executor e = // ...
for (int i = 0; i < 2000; i++) {
e.execute(new WorkerRunnable(doneSignal));
}
doneSignal.await(); // ожидание завершения всех

10
CountDownLatch. Done signal #2
public class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;

public WorkerRunnable(CountDownLatch doneSignal) {


this.doneSignal = doneSignal;
}

@Override
public void run() {
// ... выполняем работу
doneSignal.countDown();
}
}

11
CountDownLatch. Gate #1
CountDownLatch startSignal = new CountDownLatch(1);
Executor e = // ...
for (int i = 0; i < 2000; i++) {
e.execute(new WorkerRunnable(startSignal));
}
startSignal.countDown(); // разрешаем старт всех

12
CountDownLatch. Gate #2
public class WorkerRunnable implements Runnable {
private final CountDownLatch startSignal;

public WorkerRunnable(CountDownLatch startSignal) {


this.startSignal = startSignal;
}

@Override
public void run() {
startSignal.await();
// ... выполняем работу
}
}

13
Design Patterns. Singleton
Ограничивает возможность создания экземпляра класса до одного, предоставляя
статический метод для получения глобального экземпляра и делая конструктор
приватным. Является анти-паттерном и рекомендуется не использовать его.

14
Design Patterns. Singleton. Example
public class ConnectionsManager {
private static ConnectionsManager instance;

private ConnectionsManager() {
// ... сложная и длительная инициализация
}

public static ConnectionsManager getInstance() {


if (instance == null) {
instance = new ConnectionsManager();
}
return instance;
}
}
15
Concurrency. Dead lock
“Мертвая хватка”. Одна из проблем, возникающих в многопоточной
среде, в результате неправильного применения “замков” - synchronized и
других locks.

В случае возникновения ее можно решить только с помощью


перезапуска приложения.

16
Concurrency. Dead lock
В дебрях потока #1: В дебрях потока #2:

synchronized (lockOne) { synchronized (lockTwo) {


// действуем // действуем по-другому
synchronized (lockTwo) {//deadlock #1 synchronized (lockOne) {// deadlock #2
// действуем агрессивнее, // пытаемся действовать
// или только пытаемся }
} // продолжаем действовать по-другому
// продолжаем действовать }
}

17
Concurrency. Dead lock
Основные причины возникновения:
● явно неправильный порядок захвата ресурсов в рамках видимого одного метода
● использование this в качестве монитора для synchronized
○ synchronized method
○ synchronized (this)
● вызов чужих методов пока захвачен ресурс (пример)
synchronized (lock) {
// ... выполняем, например, денежный перевод, а потом:
observer.onMoneyTransferStarted(transferDetails);
// ... продолжаем выполнять
}

18
Concurrency. Scheduled executor
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

final Runnable beeper = new Runnable() {


public void run() { System.out.println("beep"); }
};

final ScheduledFuture<?> beeperTimer =


scheduler.scheduleAtFixedRate(beeper, 10, 10, TimeUnit.SECONDS);

scheduler.schedule(new Runnable() {
public void run() { beeperTimer.cancel(true); }
}, 60 * 60, TimeUnit.SECONDS);

19
Практика #1
1. Создать 2000 одновременных задач, которые увеличивают целочисленный
счетчик на 1. Подтвердить проблему атомарности. Проверить ее решение с
помощью volatile или Atomic классов.
2. Выполнить ожидание завершения задач с помощью CountDownLatch.
3. Получить доступ к singleton-объекту с “ленивой” (lazy) инициализацией из
множества потоков с использованием барьера инициализации при помощи
класса CountDownLatch. Подтвердить проблему атомарности. Решить ее
одним из известных способов.

20
Практика #2
4. Воспроизвести проблему dead lock любым способом.

5. Использовать ScheduledExecutorService для решения задачи:

Create a simple java program with 2 methods. One method prints “Hello” and the other
method prints “World”. Call these methods concurrently & asynchronously. That is, both
methods are invoked at the same instant, not one after the other. So on console you
should sometimes see “World Hello” and sometimes “Hello World” depending on which
method is invoked first. Repeat these calls every 10 seconds and stop after 1 minute.

Expected Result: On the console you should see 6 combinations, a mix of “Hello World” and
“World Hello”.

21
Домашнее задание

Хорстман К. Библиотека профессионала. Java. Том 1. Основы. 10-е издание.


● Глава 14. Параллельное программирование
○ 14.5. Синхронизация
○ 14.6. Блокирующие очереди
○ 14.7. Потокобезопасные коллекции
○ 14.10. Синхронизаторы

1. https://en.wikipedia.org/wiki/Producer–consumer_problem
2. https://github.com/iluwatar/java-design-patterns/tree/master/producer-consumer
3. SQL / Wikipedia
4. SQL Tutorial / w3schools.com

22
Спасибо за внимание

Вопросы?
23

Вам также может понравиться