Использую proprietary библиотеку. В классе main() создаю экземляр Thread-safe configurated класса из этой библиотеки. Затем, в мультитрейдинге, используя этот единственный экземляр, запускаю методы этого класса с разными параметрами. Каждому созданному потоку делаю join() . По окончанию этих методов и метода main() имею необходимый результат.
Однако даже после окончания метода main() в командной строке продолжает мигать курсор, говорящий о том что программа продолжает выполняться. Полагаю, что это потоки, запущенные методами этого класса (хотя сами методы завершили работу!). Как я могу программно завершить работу этих потоков? К исходникам доступа не имею, от этой библиотеки отказаться не могу.
Отслеживать
76.4k 6 6 золотых знаков 51 51 серебряный знак 117 117 бронзовых знаков
задан 28 мар 2016 в 12:18
121 9 9 бронзовых знаков
так как библиотека неизвестна, то рекомендую почитать документацию к ней и поспрашивать разработчиков этой библиотеки. Если и это не поможет, то посмотреть отладчиком/профайлером/дизамссемблером и разобраться. Если и это не поможет — написать библиотеку самому. В случае, если и после этого решение не будет найдено — пойти в другую компанию.
Методы sleep и join. Thread states (прокачанная Java)
28 мар 2016 в 12:25
Если вам дан исчерпывающий ответ, отметьте его как верный (галка напротив выбранного ответа).
29 мар 2016 в 6:32
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Краткая справка о потоках в Java. Потоки бывают двух видов:
- Daemon (демон) — служебный поток
- Основной поток (не-демон)
JVM завершает работу программы, когда все ее потоки, не являющиеся демонами, завершены. Как только завершается последний не-демон-поток, все демоны сразу уничтожаются. Поэтому, чтобы гарантированно завершить все потоки после завершения вашего основного потока, нужно, чтобы они были демонами. И вот тут есть удобная штука: демон-поток может порождать только демоны.
Поэтому вам нужно в мейне создать новый поток, сделать его демоном, и уже в нём запускать сторонние библиотеки. Когда ваш главный поток отработает, все демоны тут же завершатся. Пример, как это сделал я, когда столкнулся с такой же проблемой:
Только необходимо учитывать, что как только ваш main завершит работу, все демоны будут завершены (если нет других не-демонов). Советую основательно подумать, как лучше разделить потоки на основные и служебные, в случае крайней необходимости можно подождать завершения демона с помощью известного вам join() .
Источник: ru.stackoverflow.com
Урок по Java 80: Многопоточность 15: Executors
Введение в многопоточность
Есть два способа создания потоков в Java: унаследоваться от класса Thread или реализовать интерфейс Runnable.
Создание потока через наследование
Рассмотрим пример, в котором мы расширяем стандартный класс Thread, переопределяя лишь один метод run(). В нём мы ждём 3 секунды, а затем выводим сообщение о завершении потока. Обратите внимание, что запуск потока осуществляется не через метод run(), а через метод start().
Метод TimeUnit.SECONDS.sleep() полностью эквивалентен стандартному Thread.sleep(), однако в первом случае мы явно указываем единицы измерения времени, что делает код более читаемым. Я рекомендую использовать именно такую форму записи.
Поскольку метод sleep() относится к низкоуровневым, он может кидать InterruptedException, которое свидетельствует о том, что поток был прерван. Это исключение является единственным признаком того, что поток был принудительно остановлен. И чтобы не терять эту информацию в стеке вызовов, мы выставляем флаг interrupted при помощи соответствующего метода Thread.currentThread(). Такой способ обработки InterruptedException является правильным и именно его нужно использовать в подобных случаях.
В результате запуска второго потока мы сначала увидим сообщение о том, что основной поток завершён, а затем будем дожидаться завершения второго потока.
Создание потока через интерфейс
Теперь создадим поток с помощью интерфейса Runnable.
Интерфейс Runnable требует определить единственный метод run(). Для запуска потока требуется создать объект Thread, передав ему в конструктор экземпляр нашего класса. Затем у этого объекта Thread вызываем метод start(). Данный вариант является более предпочтительным, если наш класс уже наследуется от какого-то другого базового класса.
Потоки-демоны
В Java процесс завершается тогда, когда завершается последний его поток. Даже если метод main() уже завершился, но еще выполняются порожденные им потоки, система будет ждать их завершения.
Однако это правило не относится к особому виду потоков – демонам. Если завершился последний обычный поток процесса, и остались только потоки-демоны, то они будут принудительно завершены и выполнение процесса закончится. Чаще всего потоки-демоны используются для выполнения фоновых задач, обслуживающих процесс в течение его жизни.
Сделать поток демоном очень просто – достаточно установить соответствующий флаг с помощью метода setDaemon().
Несмотря на то, что поток-демон останавливается на три секунды, мы эти три секунды ждать не будем и программа завершит свою работу. При это исключение InterruptedException не возникает.
Как остановить поток
Остановить поток можно с помощью метода interrupt(). В случае, если поток не является демоном, то возникает исключение InterruptedException, о котором мы говорили выше.
Но вызов interrupt() следует толковать не как принудительную остановку, а как вежливый запрос. Немного изменим пример, чтобы показать, как должна обрабатываться остановка. Для имитации тяжёлой задачи сделаем цикл в котором будем вычислять тригонометрическую функцию.
Перед каждой итерацией цикла мы проверяем флаг Thread.currentThread().isInterrupted(). И если он оказывается установлен, мы прерываем наши вычисления. Таким образом, мы корректно обрабатываем флаг остановки потока.
Источник: devmark.ru
Java при завершении main потока автоматически завершается выполнение программы
New — состояние, когда поток создан и готов к использованию. Это состояние, когда мы еще не запустили поток.
Running — состояние, в которое поток переходит после того, как мы его запустили. В этом состоянии поток выполняет свою работу, т. е. логику, которую мы определили.
Waiting — состояние ожидания, в которое поток может перейти во время своего выполнения. Есть три состояний ожидания — Blocked , Waiting , TimedWaiting . Например, когда поток пытается получить монитор объекта, он входит в состояние блокировки — Blocked ; когда поток ожидает выполнения другого потока, тогда поток переходит в состояние ожидания — Waiting , а когда поток ожидает только определённое количество времени для выполнения другого потока, поток входит в состояние — TimedWaiting . Поток возвращается в состояние — Running , как только другие потоки выполнили или освободили монитор объекта. Поток может бесконечно менять свое состояние из состояния — Running в состояние — Waiting и наоборот.
Dead / Terminated — состояние, в которое поток переходит после завершения выполнения или в случае возникновения исключений. Поток после выполнения не может быть запущен снова. Если мы попытаемся запустить поток в состоянии — Dead / Terminated , мы получим исключение IllegalStateException .
Thread join()
Еще один полезный метод, предоставляемый классом Thread — join() . При написании многопоточных программ могут быть случаи, когда необходимо ждать завершения выполнения какого-либо потока и после этого продолжить выполнение текущего потока. В таких случаях полезно применять метод — join() . Данный метод позволяет одному потоку ожидать завершения другого.
Рассмотрим следующий пример:
Как только threadTwo начинает выполняться, метод main() продолжает свое выполнение, он печатает — «Main method executing» и завершает свое выполнение. Параллельно выполняется threadTwo , считает до 1000, потом выводит — «Counter has finished its execution, counter = 1000» и заканчивает выполнение.
Но что, если мы хотим, чтобы основной поток ждал завершения выполнения потока — threadTwo ? Как этого добиться? Довольно просто — использование join() делает то, что нужно.
Рассмотрим следующий пример:
Порядок вывода в этом примере изменился. Сразу после запуска threadTwo , основной поток вызывает метод join() у потока — threadTwo . Это приводит к тому, что основной поток переходит в состояние ожидания и ждет, пока threadTwo не завершит свое выполнение. Как видно из вывода, поток — threadTwo завершает выполнение, считает до 1000 и выводит сообщение — «Counter has finished its execution, counter = 1000» и заканчивает выполнение. После этого mainThread продолжает свое выполнение и выводит следующее сообщение — «Main method executing».
Вдобавок, если во время выполнения потока — threadTwo возникнет исключение, основной поток продолжит свое выполнение аналогично как в случае с успешным выполнением потока — threadTwo , ситуаций deadlock не будет.
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека джависта»
Daemon Threads
В Java существует два типа потоков — пользовательские (те, которые мы создаем) потоки и потоки-демоны . Когда запускается Java-программа, сразу же начинается выполняться один поток — основной поток. Основной поток запускает метод main() . Мы можем создавать новые потоки из основного потока. Основной поток завершает выполнение последним, поскольку он выполняет различные операции завершения работы c потоками ввода и вывода, отключает соединения с базами данных и т. д.
Потоки-демоны в основном функционируют как вспомогательные потоки, они выполняют разные операции в фоновом режиме. Например, Garbage Collection в Java выполняется в фоновом режиме как поток-демон .
В основном потоке мы можем создавать столько потоков, сколько необходимо. Более того, класс Thread предоставляет метод setDaemon(boolean) , который позволяет рабочему (=пользовательскому) потоку превратиться в поток-демон.
Потоки-демоны:
- поток с низким приоритетом, работающий в фоновом режиме;
- поток-демон автоматически завершается виртуальной машиной Java, когда все остальные рабочие (worker threads) потоки завершают выполнение;
- обычно потоки-демоны используются для операций ввода-вывода и сервисов (в смартфонах для связи Bluetooth или NFC).
Основное различие между пользовательским потоком и потоком-демон заключается в том, что когда все рабочие (=основные или пользовательские) потоки завершают выполнение или умирают, потоки-демоны автоматически завершаются JVM, даже если они все еще выполняются.
Thread.getName()
Давайте определим наш собственный поток-демон. Мы создадим поток-демон, который будет печатать сообщение каждую секунду.
Кроме того, setDaemon(boolean) можно вызывать только в том случае, если поток находится в состоянии New и пока ещё не запущен, иначе мы получим исключение IllegalThreadStateException .
В этой части статей о многопоточности мы обсудили жизненный цикл потока, метод Thread.join() и потоки-демоны. В следующей статье обсудим синхронизацию потоков, что такое монитор объекта и взаимодействие между потоками.
Материалы по теме
- ☕ Сертификаты и тренинги для Java-разработчика
- Путь в Java, или Зачем нужен ментор: интервью
- ☕ Учебник по Java: инкапсуляция на простых примерах
Источник: proglib.io