Java lang thread run
Почему java.lang.Thread не вызывает метод run() своего явного java.lang.Runnable при запуске?
В документах Java указано, что если при создании нового потока мы зададим объект , который можно запустить, то .start() этого потока будет выполнять метод run() предоставленного объекта, который можно запустить.
Если это так, не должен ли этот тестовый код печатать «a» (вместо печати «b»)?
8 Ответов
Потому что вы переопределяете метод Thread.run().
Вот такая реализация Thread.run() :
Реализация по умолчанию для вызова выполнимое. Однако вы переопределяете реализацию по умолчанию и NOT вызываете runnable. Самый простой способ исправить это
Реализация по умолчанию Thread.run() делает то, что говорят javadocs (посмотрите на исходный код)
Однако Thread.run() является общедоступным, и вы перешли его, поэтому он вызывает ваш println(«b») и полностью игнорирует Runnable, передаваемый в конструкторе. Возможно, Thread.run() должно быть окончательным, но это не так.
Вы фактически расширяете класс Thread и вызываете start на экземпляре этого анонимного подкласса.
Я думаю, что путаница заключается в том, что «Java Doc»-это для класса java.lang.Thread , а не для вашего класса, который расширяет этот класс.
Теперь, если он не называет это run , то java doc ошибается. Но это неправда.
Вы переопределили реализацию по умолчанию Thread.run() в том, что я предполагаю, является анонимным подклассом, поэтому то, что говорит JavaDoc, больше не применяется.
Если вы попробуете
Вы получите ответ, которого ожидаете, a .
Первый блок переопределяет run в Runnable . Второй блок переопределяет run в Thread . При вызове start на Thread вызывается его метод run . Метод run по умолчанию в Thread вызывает метод Runnable.run , но так как вы перешли Thread.run , нет кода для запуска Runnable — только ваш код для печати ‘b’.
Документация, которая у меня есть, говорит об этом: «Вызывает выполнение этого потока; виртуальная машина Java вызывает метод run этого потока. » это означает , что он должен печатать b вместо a , так как вы переопределили метод run() вместо Thread .
Вы вызываете метод start(). Читая документы по ссылке, которую вы предоставили, он утверждает::
общественного недействительными start() Заставляет этот поток начать выполнение; Java Виртуальная машина вызывает метод run этого потока.
Вы должны вызвать метод run(), если хотите вызвать метод Runnable объекта run.
public void run(), если этот поток был создан с использованием отдельного Запускаемый объект run, затем вызывается метод run этого объекта Runnable; в противном случае этот метод ничего не делает и возвращает.
Похожие вопросы:
Мне было интересно, требуется ли внешняя синхронизация для использования методов в java.lang.Thread ? Например, можно ли вызвать метод t1.isAlive() из любого потока без внешней синхронизации и.
Почему мы вызываем метод start() объекта thread, который в свою очередь вызывает метод run() , а не вызываем непосредственно метод run() ?
Во время прохождения исходного кода класса java.lang.Thread . Любопытно, что я хотел посмотреть, как метод run() (определяемый пользователем run()) вызывается классом Thread. когда я реализую.
Я разрабатываю трансформатор для Java 6 *1) , который выполняет своего рода частичную оценку , но давайте рассмотрим для простоты интерпретацию abstract-syntax-tree программы Java. Как имитировать.
Как способ выполнения определяется способ start() при нарезании резьбы? Каков внутренний механизм в методе start(), который непосредственно вызывает метод run(). Заранее спасибо.
Я пытаюсь создать базовый поток с запускаемым объектом в Java. Ниже приведен мой код: import java.lang.Thread; import java.lang.Runnable; public class TestRunnable< public static void main(String[].
Сегодня я пришел на собеседование . интервьюер попросил меня написать тему в java 1.5 .Либо вы можете расширить класс java.lang.thread, либо реализовать интерфейс java.lang.Runnable. Возможно ли.
Если метод start() потока внутренне вызывает метод run(), то почему бы нам непосредственно не вызвать метод run() в нашем коде? Какие проблемы связаны с этим процессом?
Java lang thread run
02:56:42] [Server thread/ERROR]: Encountered an unexpected exception
java.lang.NoClassDefFoundError: Could not initialize class Reflector
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:631) [MinecraftServer.class:?]
at java.lang.Thread.run(Unknown Source) [?:1.8.0_161]
[02:56:42] [Server thread/INFO]: Applying holder lookups
[02:56:42] [Server thread/INFO]: Holder lookups applied
[02:56:42] [Server thread/INFO]: The state engine was in incorrect state SERVER_STARTING and forced into state SERVER_STOPPED. Errors may have been discarded.
Ru Beta — Сервер на версии 1.7.3 Бета
Совмещаю лень и продуктивность
[?:1.8.0_221]
at java.net.InetAddress$2.lookupAllHostAddr(Unknown Source)
[?:1.8.0_221]
at java.net.InetAddress.getAddressesFromNameService(Unknown Source)
[?:1.8.0_221]
at java.net.InetAddress.getAllByName0(Unknown Source)
[?:1.8.0_221]
at java.net.InetAddress.getAllByName(Unknown Source)
[?:1.8.0_221]
at java.net.InetAddress.getAllByName(Unknown Source)
[?:1.8.0_221]
at java.net.InetAddress.getByName(Unknown Source)
[?:1.8.0_221]
at net.minecraft.server.v1_12_R1.DedicatedServer.init(DedicatedServer.java:166)
[craftbukkit-1.12.2.jar:git-Bukkit-e60fc34]
at net.minecraft.server.v1_12_R1.MinecraftServer.run(MinecraftServer.java:522) [craftbukkit-1.12.2.jar:git-Bukkit-e60fc34]
at java.lang.Thread.run(Unknown Source) [?:1.8.0_221]
[10:27:08 ERROR]: This crash report has been saved to: C:UsersэфЁхщDesktopютр яряър (5)serverCD.crash-reportscrash-2019-10-29_10.27.08-server.txt
[10:27:08 INFO]: Stopping server
ВОТ МОЙ КРАШЛОГ ЧТО ЭТО.
Дэд без репы
—- Minecraft Crash Report —-
WARNING: coremods are present:
Do not report to Forge! (If you haven’t disabled the FoamFix coremod, try disabling it in the config! Note that this bit of text will still appear.) (foamfix-0.10.10-1.12.2.jar)
ObfuscatePlugin (obfuscate-0.2.6-1.12.2.jar)
McLib core mod (mclib-1.0.4-1.12.2.jar)
PerformantLoadingPlugin (performant_1.12.2_1.7.jar)
Contact their authors BEFORE contacting forge
// I bet Cylons wouldn’t have this problem.
Time: 31.01.20 13:50
Description: Exception in server tick loop
java.net.UnknownHostException: 8805353535
at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method)
at java.net.InetAddress$2.lookupAllHostAddr(Unknown Source)
at java.net.InetAddress.getAddressesFromNameService(Unknown Source)
at java.net.InetAddress.getAllByName0(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at java.net.InetAddress.getByName(Unknown Source)
at net.minecraft.server.dedicated.DedicatedServer.func_71197_b(DedicatedServer.java
:176)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:486)
at java.lang.Thread.run(Unknown Source)
— System Details —
Details:
Minecraft Version: 1.12.2
Operating System: Windows 8.1 (amd64) version 6.3
Java Version: 1.8.0_45, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 346265688 bytes (330 MB) / 1200578560 bytes (1144 MB) up to 2112618496 bytes (2014 MB)
JVM Flags: 2 total; -Xincgc -Xmx2G
IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
FML: MCP 9.42 Powered by Forge 14.23.5.2847 24 mods loaded, 23 mods active
States: ‘U’ = Unloaded ‘L’ = Loaded ‘C’ = Constructed ‘H’ = Pre-initialized ‘I’ = Initialized ‘J’ = Post-initialized ‘A’ = Available ‘D’ = Disabled ‘E’ = Errored
Заметки программистера
IT — это прекрасно!
Страницы
Состояния java.lang.Thread на граблях и примерах
Каждый java разработчик знает что такое поток, как его запустить и, возможно, поменять ему приоритет или даже сделать его демоном. Сегодня этих поверхностных знаний зачастую достаточно для того, чтобы успешно справляться со своими повседневными задачами, в которых крутые фреймворки всеми силами пытаются скрыть от нас нюансы многопоточности. Но иногда жизнь заставляет спустится на дно на уровень ниже и познакомиться с нюансами работы с потоками более детально.
В этой статье, по мере решения простых задач, через серию проб и ошибок, мы рассмотрим некоторые нюансы при работе с классом Thread в java, поговорим о том, какие у потоков бывают состояния и при каких условиях поток переходит из одного состояния в другое.
Задача про шагающего робота
Вариант первый: разделяемое состояние.
Если запустить этот код, можно обнаружить, что прогулка нашего робота будет совсем не такой долгой как нам хотелось и в некоторых случаях может и вовсе ограничиться одним единственным шагом.
Все из-за того, что для оптимизации производительности для каждого потока создается локальная копия переменной currentLeg, изменения которой не видны другому потоку. Для решения этой проблемы существует ключевое слово volatile, которое говорит о том, что операция над переменной совершенная в одном потоке, должна быть видна в других.
Теперь два потока бегая в бесконечных циклах и пытаясь перехватить друг у друга разделяемый ресурс, решают нашу задачу.
Обратите внимание на инструкцию Thread.yield(). Метод yield переводит состояние потока из Running в Ready и позволяет планировщику переключиться на другой поток. Конкретно в нашем примере наличие или отсутствие вызова данного метода не сильно скажется на результате, но на практике может позволить сделать переключение между потоками более предсказуемым.
Но что на счет эффективности нашего решения? Наше приложение порождает два потока, которые не останавливаясь производят вычисления. Если открыть системный монитор в операционной системе, или запустить VisualVM, то можно заметить огромное потребление ресурсов CPU нашей программой. Пока один поток производит вывод в system out, другой наворачивает циклы в пустую. Чем дольше выполняет полезную нагрузку один поток, тем больше пустой работы выполняет второй:
Вариант второй: общий монитор
Было бы здорово останавливать выполнение одного потока на время работы другого, а затем просыпаться и останавливать второй поток.
В классе Thread есть методы suspend() и resume() , но они помечены как устаревшие и считаются опасными для использования.
Чтобы ответить на вопрос почему, давайте представим, что в программе, выполняющейся в потоке, есть работа с критическими ресурсами (на пример System.out), доступ к которым мы должны получать только через монитор:
Теперь нам требуется чтобы кто-то из вне вовремя будил наши потоки. Но прямая ссылка на поток не часто доступна для вызова resume() и велика вероятность того, что наши потоки так и останутся в состоянии «frozen» processes.
Альтернативой методам suspend() и resume() являются методы wait() , notify() и notifyAll() .
Метод wait() переводит поток в состояние Waiting (или Timed Waiting, если указан таймаут ожидания), а методы notify() и notifyAll() возвращают его в состояние Runnable.
Важно понимать, что это методы не класса Thread, а класса Object который может быть легко расшарен между потоками, что позволяет избежать вышеописанных трудностей с методами suspend и resume.
Теперь мы можем разделить между нашими потоками общий монитор и сделав шаг будить всех его владельцев, после чего спокойно начинать ждать пока нас кто-нибудь разбудит:
Это позволяет избежать взаимных блокировок, когда оба потока уходят в ожидание одного и того же монитора. Конкретно в нашем примере велика вероятность того, что оба потока одновременно вызовут метод notify, а затем вместе уснут, и некому будет их разбудить.
Важно не забывать об этом, тк к сожалению, компилятор не может вам об этом напомнить, а вспоминать об этом в в runtime очень не приятно!
С другой стороны метод wait выполняется внутри блока синхронизации и может создаться впечатление, что поток уже никогда не освободит монитор. Но на самом деле это не так. Все дело в том, что метод wait освобождает монитор, а проснувшись, поток попадает в состояние ожидания монитора, который он освободил перед сном.
Раз засыпая поток освобождает монитор, то таких заснувших потоков может быть много — отсюда существование метода notifyAll() и замечание о том, что метод notify() пробуждает случайный из спящих потоков.
Теперь наше решение выглядит куда более разумным, ведь мы избавились от пустой траты ресурсов на выполнение холостых циклов!
Потокам тоже снятся страшные сны
Теперь, из-за непреднамеренного просыпания, может случиться непоправимое! Наш робот может сорваться вниз*:
- Левая рука схватила канат и уснула
- Правая рука разбудила левую и схватила канат
- Левая рука проснулась
- Левая рука отпустила канат
- Неожиданно проснулась правая рука!
- Правая рука отпустила канат
- Крах!
*Пример приведен чисто теоретический, для наглядной демонстрации последствий описываемой проблемы. Мне не удавалось (да я и не сильно пытался) воспроизвести описываемую последовательность событий на практике
Чтобы это предотвратить, документация советует нам вызывать метод wait в цикле с явной проверкой необходимости проснуться:
Как остановить поток?
У многих начинающих изучать потоки в java людей возникает вопрос: как в общем случае мне остановить поток? Короткий ответ: никак.
Не смотря на то, что в классе Thread есть подходящий метод stop() , он отмечен как устаревший и вообще предан жесточайшей анафеме!
Почему? Одна из причин заключается в том, что поток может быть остановлен в момент владения монитором и проведения критических операций, в результате чего изменяемый объект будет доступен во вне в непредсказуемом состоянии.
Для примера рассмотрим пример с денежными счетами и переводом средств между ними:
Метод join() — это метод для ожидания завершения работы потока. Он переводит поток, в котором был вызван, в состояние Waiting до тех пор, пока не завершится тот поток, для которого этот метод был вызван.
Если попытаться остановить первую транзакцию с помощью метода stop() сразу после запуска, то есть вероятность того, что поток будет прерван после списания средств с первого счета, но до перевода их на второй и мы получим неприятную ситуацию с исчезновением средств в системе, что приведет к исключению во время проведения второй транзакции.
Как быть, если нам очень надо сделать транзакции прерываемыми и все таки останавливать потоки? В таком случае мы должны уметь понять, что транзакцию пытаются прервать и предпринять меры по откату уже произведенных операций.
Чтобы указать потоку, что его работа должна быть прервана, существует метод interrupt(). Если поток на момент вызова метода interrupt() находился в ожидании выполнения метода wait(), sleep() или join(), будет сгенерировано исключение InterruptedException. При этом, если поток выполнял вычисления (например бегал в цикле), то эти вычисления не будут прерваны, а поток просто будет отмечен как прерванный.
Не глотайте бездумно InterruptedException!
Здесь и ранее, допущена ошибка в обработке исключения InterruptedException из метода wait() . Проблема заключается в том, что при возникновении этого исключения поток не помечается как прерванный и цикл в нашем примере не будет прерван!
К сожалению, исключение InterruptedException создано как checked, и должно либо фигурировать в сигнатуре метода run() , либо явно обработано внутри метода. Перехватив это исключение мы не должны его просто игнорировать, нам необходимо самостоятельно отметить поток как прерванный:
Разница между Thread.start () и Thread.run () в Java
В концепции многопоточности Java start () и run () являются двумя наиболее важными методами. Ниже приведены некоторые различия между методами Thread.start () и Thread.run () :
- Создание нового потока : когда программа вызывает метод start () , создается новый поток, а затем выполняется метод run () . Но если мы напрямую вызовем метод run (), то новый поток не будет создан, а метод run () будет выполнен как обычный вызов метода для самого текущего вызывающего потока, и многопоточность не будет выполняться.
Позвольте нам понять это на примере:
class MyThread extends Thread <
public void run()
System.out.println( «Current thread name: «
System.out.println( «run() method called» );
public static void main(String[] args)
MyThread t = new MyThread();
Как мы можем видеть в приведенном выше примере, когда мы вызываем метод start () нашего экземпляра класса потока, создается новый поток с именем по умолчанию Thread-0, а затем вызывается метод run () и все внутри него выполняется на вновь созданная тема.
Теперь давайте попробуем вызвать метод run () напрямую вместо метода start () :
class MyThread extends Thread <
public void run()
System.out.println( «Current thread name: «
System.out.println( «run() method called» );
public static void main(String[] args)
MyThread t = new MyThread();
Как видно из приведенного выше примера, когда мы вызывали метод run () нашего класса MyThread, новый поток не создавался, а метод run () выполнялся в текущем потоке, то есть в основном потоке. Следовательно, не было многопоточности. Метод run () вызывается как обычный вызов функции.
Многократный вызов: в концепции многопоточности Java другое наиболее важное различие между методом start () и run () состоит в том, что мы не можем дважды вызывать метод start (), в противном случае он вызовет исключение IllegalStateException, тогда как метод run () может быть вызван несколько раз, так как это просто нормальный вызов метода.
Позвольте нам понять это на примере:
class MyThread extends Thread <
public void run()
System.out.println( «Current thread name: «
System.out.println( «run() method called» );
public static void main(String[] args)
MyThread t = new MyThread();
Выход :
Как мы можем видеть в приведенном выше примере, вызов метода start () снова вызывает исключение java.lang.IllegalThreadStateException .
Теперь давайте попробуем вызвать метод run () дважды:
class MyThread extends Thread <
public void run()
System.out.println( «Current thread name: «
System.out.println( «run() method called» );
public static void main(String[] args)
MyThread t = new MyThread();
Как видно из приведенного выше примера, вызов метода run () дважды не вызывает никаких исключений и выполняется дважды, как и ожидалось, но в самом основном потоке.
Runnable и Thread
В Java многопоточность программы организуется с помощью интерфейса Runnable и класса Thread, который наследуется от Runnable. Первый способ более гибкий, второй – проще.
Та часть кода, которая должна выполняться в отдельном потоке, выносится в свой класс, имеющий переопределенный метод run(). Код метода run() выполняется, когда к объекту типа Thread применяется метод start(). Непосредственный вызов run() новый поток не создает.
Здесь обработка исключений необходима из-за статического метода sleep(), который приостанавливает выполнение текущего потока. Данный метод часто используют в дочерних потоках, когда они должны выполнять какое-либо действие постоянно, но не бесперебойно. Например, периодически проверять доступность ресурса.
Метод join() заставляет текущий поток ждать завершения нити, к которой применяется. Только после этого текущий поток может продолжить выполнение своего кода.
В данном случае мы создаем класс-наследник от Runnable. Объект типа Runnable или его производное передается в конструктор объекта типа Thread. После этого поток запускается.
Другой вариант – когда пользовательский класс является наследником Thread:
Этот вариант не подходит, если класс для организации отдельного потока должен наследоваться от другого класса (не Thread). Поскольку в Java нет множественного наследования классов, приходится использовать наследование от интерфейса Runnable. Также данный подход не дает возможности запускать несколько потоков на основе одного объекта. Так в первом примере мы могли бы передать единственный объект anotherRun в несколько объектов типа Thread.
Напомним, библиотечный класс Thread сам является наследником Runnable.
Если в отдельный поток обособляется небольшая подзадача, можно использовать неименованный класс:
Прерывание потоков
Для прерывания выполнения нити, если это необходимо, используется метод interrupt(), который устанавливает переменную isInterrupt в значение true. К коде пользовательского класса, унаследованного от Runnable/Thread, это переменная должна проверяться. Отсюда следует, что на самом деле в Java нет возможности прервать поток извне, поток может остановиться только сам.
С другой стороны, в метод sleep() уже встроена проверка переменной isInterrupt, поэтому проверку вручную опускают. Если sleep() считывает наличие прерывания, то генерирует исключение.
В примере основной поток ожидает ввод данных, в это время выполняется вторая нить. Но как только вы нажмете Enter, выполнится метод interrupt(). В свою очередь метод sleep() прочитает значение переменной isInterrupt класса Thread и сгенерирует исключение InterruptedException.
Если sleep() не используется, то isInterrupt проверяется вручную методом isInterrupted(). Следующий пример содержит ошибку, приводящую к зацикливанию:
Мы могли бы ожидать, что через 2 секунды сработает метод interrupt(), который прервет дочернюю нить. Однако, поскольку в ней не проверяется значение isInterrupt, цикл продолжает работать. Корректный код может выглядеть так:
При наследовании от Runnable текущий поток через this получить нельзя. Его получают, вызывая соответствующий метод класса Thread: