Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Java: Общие вопросы > Нити |
Автор: jGorets 22.9.2010, 00:47 |
Кто как или где и в каких случаях лучше создавать потоки через интерфейс Runnable или используя класс Thread?? |
Автор: soulcub 22.9.2010, 06:44 |
То-есть вопрос можно перефразировать как "Зачем нужны потоки?". Не люблю отвечать ссылками, но вопрос то ведь элементарный.. http://www.quizful.net/post/java-threads Тут неплохо написано про потоки. |
Автор: nc30 22.9.2010, 10:26 | ||||||||
В двух словах: наследовать от Thread - более просто, реализовать Runnable - более гибко. Создать потоковый класс наследованием от класса Thread весьма просто: наследовал класс, переопределил метод run(), создал объект, запустил на выполнение. Но наследовать наш потоковый класс от какого-либо другого мы уже не сможем, т. к. родитель в Java может быть только один и он уже есть - класс Thread. Реализация интерфейса Runnable чуть более сложна в описании и требует несколько большего количества ресурсов. Все это происходит оттого, что класс, реализующий интерфейс Runnable - это еще не полноценный потоковый класс. Реализуя Runnable (определяя метод run()), мы объявляем, что объект теоретически способен к выполнению (able to be run), но настоящих потоковых методов (start(), wait() и еще с полсотни методов) у нашего класса нет. Для того, чтобы объект нашего класса действительно мог выполняться, ему нужен т. н. handler - управляющий поток. Handler - это полноценный поток, объект класса Thread. Образно говоря, он "взваливает" объект нашего класса (который - как мы уже выяснили - не совсем поток) к себе "на плечи" и полностью им управляет в отношении "потоковости", т. е. потоковые методы выполняются как раз handler'ом. К членам нашего класса, которые непосредственно не связаны с его "потоковостью", handler отношения не имеет. Другими словами, наш класс, реализуя интерфейс Runnable, заявляет, что он готов исполнить "потоковый" контракт, а handler момогает ему этот контракт выполнить. Все на самом деле очень просто ![]() Соответственно в этом случае (реализация Runnable), приходится чуть больше описывать. К тому же вместо одного объекта (как в случае наследования от Thread) создаются два - наш "поток" и управляющий поток. Зато этот вариант не связывает нам руки в отношении возможности наследования нашего класса, если это необходимо. А какой вариант выбрать - зависит от конкретной ситуации при разработке программы.
|
Автор: Skipy 22.9.2010, 12:57 |
Добавлю пару слов. Иногда необходимо вызвать извне методы класса Thread, чаще всего это join и interrupt. В этом случае по любому где-то придется держать ссылку именно на Thread. В случае, когда ссылка нужна еще и на экземпляр нашей реализации потока - например, выставить какой-нибудь флаг, забрать какие-нибудь данные и т.п. - имеет смысл их совместить при возможности, и унаследоваться от Thread. |
Автор: nc30 22.9.2010, 13:12 | ||||||
Skipy, спасибо вам за дополнение ![]()
Это то, о чем вы говорите?
Не очень понял, что именно имеет смысл совместить. Можно чуть подробнее? |
Автор: Skipy 22.9.2010, 16:52 | ||||||
Не совсем. Внутри Вашего Runnable ссылку на поток получит ьне вопрос - Thread.currentThread(); Эта ссылка нужна вовне:
Ссылка t может понадобиться не сразу, а где-то в дальнейшем, когда надо прервать, например, этот поток, а он висит на мониторе. Флагом Вы этого не сделаете, а вот вызовом t.interrupt() ожидание на мониторе прерывается.
Допустим, Вы создали реализацию Runnable, и Вам необходимо иметь возможность вызывать у нее какие-то методы. Т.е. Вам нужно сохранить ссылку на экземпляр этой реализации. С другой стороны, Вам нужно иметь ссылку на поток, в котором этот экземпляр работает. Т.е. ссылок получается уже две, вп римере выше - это myRunnable и t. Вот чтобы не хранить две ссылки и не протаскивать их везде, где они нужны, можно унаследовать класс от Thread (а не от Runnable), и тогда ссылка нужна будет всего одна. |
Автор: nc30 22.9.2010, 17:15 | ||||||||
Хорошо, предположим, есть класс:
В чем тогда ощутимое преимущество
перед
Управляющий поток, по сути, один и тот же. Просто во-втором случае ссылка на него хранится в самом объекте, что удобно. Или что-то здесь не так? ![]() |
Автор: LSD 22.9.2010, 20:08 |
В том что MyRunnable может быть унаследован от любого класса. |
Автор: nc30 23.9.2010, 07:21 | ||||
LSD, Skipy Да не вменятся мне мои посты в полемику с метрами программирования, просто действительно хочу разобраться ![]()
В примере выше, и в том, и в другом случае MyRunnable - один и тот же класс, он не наследует от Thread, а реализует интерфейс Runnable. Вопрос был несколько в другом. Skipy отметил следующее:
По логике поставленной передо мной задачи: 1) неудобно наследоваться от Thread; 2) неудобно хранить две ссылки и протаскивать их везде, где они нужны; 3) тем не менее иметь доступ к основному объекту и управляющему потоку. Что мне мешает сделать ссылку на управляющий тред в самом классе (см. мой последний пост) и управлять моим потоком через эту ссылку? В этом случае хранить и протаскивать (по отношению к основному объекту) нужно будет только ссылку на MyRunnable, и я, по идее, избегаю вышеперечисленных неприятностей. |
Автор: Skipy 23.9.2010, 09:18 | ||||
В философии. Как только Вы открыли setHandler - его может использовать кто угодно. Не разобравшись, поставит туда другой поток. Кроме того, Можно вызвать getHandler до того, как будет вызван setHandler. Всё это лишние потенциальные проблемы. Если Вам неудобно наследоваться от Thread, но нужно иметь его экземпляр - хранение handler-а в самом Runnable есть хороший вариант. Я бы все-таки тогда сделал так:
Это лучше хотя бы тем, что Вы не светите set-метод, во-первых, и не позовете get-метод до инициализации handler-а во-вторых. |
Автор: nc30 23.9.2010, 10:01 | ||
Skipy, позволю себе еще уточнение. Какие минусы вы видите в следующей реализации?
Что меня привлекло в этом варианте: 1) Управляющий поток создается одновременно с основным объектом. Т. о. нет риска вызвать getHandler() до создания самого handler'а. 2) После создания потока (но перед его стартом) мы можем с ним проделать какие-то подготовительные действия (obj.getHandler().setPriority(), например). 3) Метод setHandler() действительно не нужен "во избежание" (в примере выше он использован для "похожести" наших ситуаций), здесь я с вами полностью согласен. |