Подтвердить что ты не робот

Почему Thread реализует Runnable?

Метод Java Thread run() вызывается JVM в этом потоке, когда начинается поток. Чтобы дать потоку что-то сделать, вы можете сделать подкласс Thread и переопределить его метод run() или (предпочтительно), вы можете предоставить Runnable в конструктор потока. Это прекрасно.

Я был посреди создания подкласса Thread и overriding run, и я понял, что не могу сделать метод защищенным, как я ожидал, потому что Thread.run() является общедоступным. Тогда я понял, почему: он должен быть общедоступным, потому что Thread реализует Runnable. Но почему он реализует Runnable?

Это не кажется логичным. Поток запускается (из текущего потока), но вы не запускаете его так же, как вы запускаете() Runnable (из текущего потока); поток запускается сам (по собственному потоку). Если вы вызываете метод запуска Thread вручную, то вы не используете его как Thread, просто тяжелый Runnable.

Из-за конструкции любой код с доступом к объекту Thread может вызывать его метод публичного запуска и потенциально вызывать код, который не предназначен для публичного или предназначенного для вызова таким образом. Это также позволяет очень странно такие вещи:

Thread.currentThread.run();

Существует ли законное использование для реализации Thread Runable, которое я не вижу?

4b9b3361

Ответ 1

Причина - "обратная совместимость".

Класс Thread создан в Java 1.0... или ранее. В те дни у Java не было внутренних классов, и поэтому не было легкого способа реализовать экземпляр Runnable. Если вы посмотрите на старые примеры потоков и учебные пособия той эпохи, обычно встречаются классы, расширяющие Thread и переопределяющие метод run().

Со временем было осознано, что расширение Thread не является хорошей идеей (по разным причинам). Однако дизайн Thread не мог быть изменен, потому что это сделало бы старый код Java несовместимым с более новыми JVM.


Существует ли законное использование для реализации Thread Runable, которое я не вижу?

Это зависит от того, что вы подразумеваете под "законным".

  • Старый код, который был написан в первые дни, не является "незаконным" в силу того, что он делает все по-старому. В этом нет ничего "сломанного".

  • Существуют потенциально сценарии, когда имеет смысл расширить Thread и переопределить метод run(). Например, вы можете захотеть run() реализовать специальный механизм для передачи информации в или из поставляемого Runnable или реализовать некоторую специальную обработку исключений или... сделать поток "перезагруженным".

  • Могут быть даже сценарии, где вы хотите вызвать run() непосредственно на объект потока. Например, если вам был передан код "собак завтрак", который расширил Thread, и вам пришлось преобразовать его для запуска в пуле потоков без изменения исходного кода. Вы можете подумать о том, чтобы создать экземпляр класса крутильных потоков и передать экземпляры в качестве runnables в threadpool для запуска. (Да... ужасно!)

Ответ 2

Переопределение запуска не объясняет, почему это когда-либо должно быть общедоступным.

Если это ваша проблема, я думаю, что для этого существует простой ответ: методы, реализующие интерфейс, всегда должны быть общедоступными в java. Было бы возможно, если бы абстрактный класс был защищен, но если Thread был абстрактным, вы бы не смогли использовать его как таковой.

Что касается того, почему Thread, реализующий Runnable, в первую очередь, java должен иметь способ узнать, в какой части поток фактически выполняет свою работу. Для этого они используют метод run. Они могли бы более четко разделить логику простого внедрения Runnable и реализовать реализацию подкласса Thread, но это то, что я считаю незначительной ошибкой, которую трудно изменить из-за исторических причин.

TL;DR; AFAIK действительно не является хорошей причиной для Thread для реализации Runnable, но есть причины для того, чтобы он реализовал какой-то интерфейс вроде этого - у них должен был быть какой-то отдельный интерфейс, такой как ThreadRunnable, вместо того, чтобы использовать тот же Runnable interface, который также использует другие функции.

Обратите также внимание на то, что на современной Java вы, вероятно, должны использовать Callables и FutureTasks вместо Threads. "Интеграция тайм-аутов, правильная отмена и объединение потоков современной поддержки concurrency для меня гораздо более полезны, чем груды необработанных потоков". Цитируя другой ответ здесь, в stackoverflow.