В продолжении цикла статей “Как не вылететь из IT через 5 лет” мы рассмотрим многопоточность.
Всё, что будет описано в данной статье, касательно технических аспектов, будет относиться исключительно к языку программирования Java. Возможно вы сможете увидеть аналогии с другими языками программирования. Ниже будут озвучены базовые моменты многопоточности в Java, которые дадут общее понимание вопроса.
Итак, приступим.
На текущий момент, практически любой крупный enterprise проект не обходится без применения многопоточности, так как это позволяет нам полностью использовать возможности “железа”. Именно поэтому, так важно глубокое понимание базовых принципов. С опытом это понимание должно только углубляться.
Что необходимо чётко понимать:
- Реализация многопоточности в вашем языке программирования
- Проблемы, которые возникают при разработке многопоточных приложений
- Способы избежания этих проблем
Реализация многопоточности в Java
Java предоставляет возможность создавать многопоточные приложения, в которых различные потоки выполняются одновременно. При этом стоит отметить, что базовые инструменты языка являются слишком низкоуровневыми. Так, например, крайне не просто корректно применять ключевые слова volatile, synchronized, методы wait(), notify() и notifyAll(). Как разработчики, мы нуждаемся в сущностях более высокого уровня (пул потоков, мониторы, семафоры и т.д.)
Для этого, ещё в 2002 году был создан JSR-166. В данном запросе были изложены требования к подобным высокоуровневым сущностям. Начиная с Java 5 они были имплементированы в Java.
На момент написания статьи (Август 2017 года) более подробно с утилитами многопоточности можно ознакомиться на сайте Oracle:
http://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/
Пакет java.util.concurrent который включает в себя общие сущности, которые используются при многопоточном программировании (ConcurrentHashMap, Semaphore, и т.д.), также включает в себя два других важных пакета:
- java.util.concurrent.locks
Включает в себя низкоуровневые механизмы для блокировки и ожидания, которые имеют отличия от мониторов и синхронизаторов (ReentrantLock, ReentrantReadWriteLock и т.д.) - java.util.concurrent.atomic
Включает в себя низкоуровневые классы для потокобезопасной разработки (AtomicLong, AtomicBoolean и т.д.)
В Java также имплементирована аппаратная инструкция CAS (compare-and-swap), которая поддерживается большинством современных процессоров. Она применяется в классе ReentrantLock, который, благодаря легковесности CAS механизма, работает более эффективно, чем монитор, который будет основан на синхронизации.
Проблемы, которые возникают при разработке многопоточных приложений
На самом деле, проблем может быть много, но мы рассмотрим только две основные, с которыми разработчикам приходится сталкиваться наиболее часто:
Deadlock, который возникает, когда нам необходимо заблокировать доступ к ресурсу. Для того, чтобы другой поток не смог получить доступ до окончания выполнения ресурсом, необходимых вычислений.
Самый простой пример, когда 2 потока ожидают вычислений друг от друга.
Здесь мы можем провести аналогию с водопроводчиками и электриками.
Представим, что “затопило” проводку. Электрики боятся начать работу, пока водопроводчики не устранят течь, а водопроводчики не горят желанием получить заряд бодрости на весь день :).
Race condition – ситуация, когда два и более потока пытаются получить доступ к открытым данным и изменять их одновременно. И здесь всё начинает зависеть от того, кто первым получил доступ к потоку.
Способы избежания этих проблем
Deadlock – всегда отслеживать в каком порядке мы получаем доступ к ресурсам. Т.е. мы должны чётко понимать, что происходит в нашей системе.
Race condition – всегда отслеживать, что мы корректно организовываем доступ к ресурсам (synchronized и другие утилиты параллелизма). Например, мы можем сделать файл доступным для чтения в любой момент, но ограничить доступ к записи и т.д.
Список материалов для изучения:
- Java Language Specification 8
- Java Concurrency in Practice (крайне рекомендую ознакомиться с данной книгой – найти PDF версию не проблема).
На этом мы заканчиваем обзор многопоточности в рамках цикла “Как не вылететь из IT через 5 лет”.
Ссылки на статьи из цикла:
Alexandr Naumov
Спасибо Евгений за статью! И если можно вопрос. В enterprise приложениях зачастую разработчик использует фреймворк (Spring , Hibernate и т.д) в них же многопоточность зашита? В каком случае приходится реально самому работать с потоками и лезть под капот? Или всю мощь многопоточности используют когда исключительно персонально под проект пишут какие то модули?
Евгений Сулейманов
Спасибо за отзыв, Александр.
Обычно, это реализовывается вручную.
Eugene Korchmar
Сегодня нашел Ваш сайт. Спасибо, много интересных часов обеспечено))
Евгений Сулейманов
Спасибо за отзыв, Евгений 🙂
Ki Ka
Хотелось, чтоб автор не бросал блог и продолжал также, доступным языком объяснять бытие программирования.