Что такое поток?
Поток – это легковесный процесс.
Преимущества использования потоков
- Использование нескольких ядер или процессоров повышает пропускную способность за счет более эффективного использования ресурсов процессора/процессоров.
- Уменьшение времени ожидания ресурсов. Один ресурс ожидает завершения операции I/O, другой может в это время использовать процессор.
- Использование потоков упрощает моделирование. Примеры: Servlet, RMI.
- Использование потоков упрощает обработку асинхронных событий.
- Более отзывчивый GUI. Event Dispatcher Thread (EDT).
Риски, которые возникают при использовании потоков
- Угрозы безопасности. Порядок операций в многопоточной программе непредсказуем. Пример: i++ состоит из трех операций – чтение, инкремент, запись. Race condition – состояние гонки. Потоки делят адресное пространство.
- Угрозы жизнеспособности. Безопасность – “ничего плохого не происходит”. Liveness (жизнеспособность) – “что-то хорошее в конце концов происходит”. Один из самых известных видов liveness-угроз – deadlock – когда потоки не могут продолжить выполнение и не могут завершиться в результате взаимной блокировки. Другие примеры угроз жизнеспособности: starvation – голодная смерть, голодание; livelock – активная блокировка.
- К жизнеспособности прямое отношение имеет производительность. Что-то хорошее не только должно происходить, но должно происходить быстро. Проблемы, которые нужно рассмотреть при разработке многопоточного приложения: малый срок службы, отзывчивость, пропускная способность, потребление ресурсов, масштабируемость. Частое переключение контекста может быть нежелательным и отрицательно влиять на производительность.
Фреймворки приносят многопоточность в приложения посредством вызова компонентов приложения из потоков фреймворка.
Потокобезопасность
shared – переменная может быть доступна из множества потоков.
mutable – значение может изменяться во время жизненного цикла объекта.
Написание многопоточного кода – это не только забота о потоках и блокировках. Написание многопоточного кода – это управление доступом к состоянию, в частности к раздеяемому (shared) и изменяющемуся (mutable) состоянию. Состояние объекта – это данные, сохраненные в переменных состояния (state variables) – в полях экземпляра или в статических полях. Состояние объекта может складываться из полей, которые находятся в других, связанных, объектах.
Если более чем один поток имеет доступ к переменной состояния, и хотя бы один поток может писать в эту переменную, то все они должны координировать доступ, используя синхронизацию.
Механизм синхронизации в Java подразумевает использование ключевого слова synchronized, которое предоставляет эксклюзивную блокировку, однако к понятию “синхронизация” также относятся такие вещи как volatile-переменные, явные блокировки, и атомарные переменные.
Если несколько потоков имеют доступ к изменяемой переменной состояния без синхронизации, значит программа не корректна. Это можно исправить тремя способами:
- Не разделять переменную состояния между потоками.
- Сделать переменную состония неизменной.
- Использовать синхронизацию.
Намного проще проектировать класс потокобезопасным, чем модифицировать его позже.
При разработке потокобезопасного класса, хорошие техники объектно-ориентрированного проектировани – инкапсуляция, неизменность и чистая спецификация инвариантов – ваши лучшие друзья.
Источники
- Java Concurrency in Practice. Brian Goetz.

