2013-08-19 4 views
31

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

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

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

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

Thread.currentThread.run(); 

Есть ли законное использование для Thread реализующего Runnable, что я не вижу?

+1

@cHao - Если вы поняли историю и цели Java (в частности, цель запуска кода, которому много десятилетий), вы поймете, что они делают все возможное, что по-человечески возможно в сложившихся обстоятельствах. –

ответ

23

Причина заключается в «обратной совместимости».

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

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


Есть ли законное использование для Thread реализующего Runnable, что я не вижу?

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

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

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

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

+0

Overriding run не объясняет, почему это когда-либо должно быть публичным. – Boann

+0

@Boann - но какой вред? Кроме того, см. Вторую маркерную точку. Это было, конечно, законным в прежние времена. –

+2

Ущерб в том, что независимо от того, является ли один из подклассов Thread или подклассом Runnable и передает его конструктору Thread, невозможно предотвратить публичный вызов кода через Thread.run. Плюс это запутывает, что существуют и обычные методы 'run()' и 'start()' Thread, и вызов неправильного может все еще работать. Кроме того, хотя я пришел на Java после добавления внутренних классов, я не вижу, как их отсутствие облегчает создание подкласса Thread, чем подкласс Runnable. – Boann

4

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

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

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

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

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

+3

«есть причины для его реализации какого-то интерфейса, такого как« Uh, например? – Boann

+1

+1 особенно для упоминания того, что методы интерфейса должны быть общедоступными в Java. – Stephan

+0

@Stephan: Зачем вам отвечать за предоставление информации, которая уже есть в вопросе? Для меня это больше похоже на причину * downvote *. , , – ruakh