1. Обзор
Хотя многопоточность помогает повысить производительность приложения, она также сопряжена с некоторыми проблемами. В этом руководстве мы рассмотрим две такие проблемы, взаимоблокировку и активную блокировку , с помощью примеров Java.
Хотя многопоточность помогает повысить производительность приложения, она также сопряжена с некоторыми проблемами. В этом руководстве мы рассмотрим две такие проблемы, взаимоблокировку и активную блокировку , с помощью примеров Java.
Программы на Java довольно часто добавляют задержку или паузу в своей работе. Это может быть полезно для стимуляции задачи или для приостановки выполнения до завершения другой задачи.
В этом руководстве будут описаны два способа реализации задержек в Java.
потоков
Когда программа Java запускается, она порождает процесс , который выполняется на хост-компьютере. Этот процесс содержит как минимум один поток — основной поток , в котором выполняется программа. Кроме того, Java поддерживает многопоточность , что позволяет приложениям создавать новые потоки, работающие параллельно или асинхронно с основным потоком.
В этом уроке мы рассмотрим, как мы можем печатать четные и нечетные числа, используя два потока.
Цель состоит в том, чтобы напечатать числа по порядку, в то время как один поток печатает только четные числа, а другой поток печатает только нечетные числа. Для решения проблемы мы будем использовать концепции синхронизации потоков и межпотокового взаимодействия.
Потоки — это легкие процессы, которые могут выполняться одновременно. Параллельное выполнение нескольких потоков может быть хорошим с точки зрения производительности и использования ЦП, поскольку мы можем работать над более чем одной задачей одновременно через разные потоки, работающие параллельно.
Инфраструктура ExecutorService
упрощает обработку задач в нескольких потоках. Мы собираемся проиллюстрировать несколько сценариев, в которых мы ждем, пока потоки закончат свое выполнение.
Кроме того, мы покажем, как корректно закрыть ExecutorService
и дождаться завершения выполнения уже запущенных потоков.
Исполнителя
При использовании Executor
мы можем закрыть его, вызвав методы shutdown()
или shutdownNow() .
Хотя он не будет ждать, пока все потоки перестанут выполняться.
В этой статье мы увидим, как иногда ложное совместное использование может обернуть многопоточность против нас.
Во-первых, мы собираемся начать с теории кэширования и пространственной локальности. Затем мы перепишем параллельную утилиту LongAdder и сравним ее с реализацией
java.util.concurrent
. На протяжении всей статьи мы будем использовать результаты тестов на разных уровнях, чтобы исследовать влияние ложного обмена.
Часть статьи, связанная с Java, сильно зависит от расположения объектов в памяти. Поскольку эти детали макета не являются частью спецификации JVM и оставлены на усмотрение разработчика , мы сосредоточимся только на одной конкретной реализации JVM: HotSpot JVM. Мы также можем использовать термины JVM и HotSpot JVM взаимозаменяемо на протяжении всей статьи.
Платформа fork/join была представлена в Java 7. Она предоставляет инструменты, помогающие ускорить параллельную обработку, пытаясь использовать все доступные процессорные ядра, что достигается с помощью подхода «разделяй и властвуй » .
На практике это означает, что фреймворк сначала «разветвляется» , рекурсивно разбивая задачу на более мелкие независимые подзадачи, пока они не станут достаточно простыми для асинхронного выполнения.
После этого начинается часть «объединения» , в которой результаты всех подзадач рекурсивно объединяются в один результат, или в случае задачи, которая возвращает void, программа просто ждет, пока каждая подзадача будет выполнена.
Чтобы обеспечить эффективное параллельное выполнение, платформа fork/join использует пул потоков, называемый ForkJoinPool
, который управляет рабочими потоками типа ForkJoinWorkerThread
.
Вилка присоединения к пулу
В этом уроке мы узнаем о Future
. Интерфейс, существующий со времен Java 1.5, может быть весьма полезен при работе с асинхронными вызовами и параллельной обработкой.
фьючерсов
Проще говоря, класс Future
представляет будущий результат асинхронного вычисления. Этот результат в конечном итоге появится в Будущем
после завершения обработки.
Давайте посмотрим, как писать методы, которые создают и возвращают экземпляр Future .
Поток — это основная единица параллелизма в Java. В большинстве случаев пропускная способность приложения увеличивается, когда создается несколько потоков для параллельного выполнения задач.
Однако всегда есть точка насыщения. В конце концов, пропускная способность приложения зависит от ресурсов процессора и памяти. После определенного предела увеличение количества потоков может привести к переполнению памяти, переключению контекста потока и т. д.
Таким образом, хорошей отправной точкой при устранении неполадок с нехваткой памяти в приложении Java является мониторинг количества потоков. В этом руководстве мы рассмотрим некоторые способы проверки количества потоков, созданных процессом Java.
В этом кратком руководстве мы узнаем о java.lang.IllegalMonitorStateException.
Мы создадим простое приложение отправитель-получатель, которое генерирует это исключение. Затем мы обсудим возможные способы предотвращения этого. Наконец, мы покажем, как правильно реализовать эти классы отправителя и получателя.
Исключение IllegalMonitorStateException
связано с многопоточным программированием в Java. Если у нас есть монитор
, на котором мы хотим синхронизироваться, это исключение генерируется, чтобы указать, что поток пытался ждать или уведомить другие потоки, ожидающие на этом мониторе, не владея им. Проще говоря, мы получим это исключение, если вызовем один из методов wait()
, notify( )
или notifyAll()
класса Object
за пределами синхронизированного
блока.