package com.javarush.task.task16.task1633;
/*
Отдебажим все на свете
*/
public class Solution {
public static Thread.UncaughtExceptionHandler handler = new OurUncaughtExceptionHandler();
public static void main(String[] args) {
TestedThread commonThread = new TestedThread(handler);
Thread threadA = new Thread(commonThread, "Нить 1");
Thread threadB = new Thread(commonThread, "Нить 2");
threadA.setUncaughtExceptionHandler(handler);
threadB.setUncaughtExceptionHandler(handler);
threadA.start();
threadB.start();
threadA.interrupt();
threadB.interrupt();
}
public static class TestedThread extends Thread {
public TestedThread(Thread.UncaughtExceptionHandler handler) {
setUncaughtExceptionHandler(handler);
start();
}
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException("My exception message");
}
}
}
public static class OurUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName() + ": " + e.getMessage());
}
}
}
если уж и создаватб объект класса Thread, то точно не при помощи объекта наследника класса Thread, а реализации функционального интефейса Runnable, но окей, раз мы читирим так и используем доп функионал, то я считаю, что в нём нужно использовать Thread.setDefaultUncaughtExceptionHandler(), и не только, чтобы обосновать читерство в создании объекта Нити, но и для того, чтобы избежать дублирования кода.
но если этого не сделать и потом еще для каждого объекта устанавливать свой обработчик (который по сути будет одним по заданию), то смысл в создании класса TestedThread с наследованием от Thread полностью исчезает.
Почему я не прав?Денис Кокшаров
1 уровень
не согласен с правильным решением
Решен
Комментарии (17)
- популярные
- новые
- старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Денис Enterprise Java Developer
4 марта 2025, 12:05
В программировании нет единственно верного решения. Любой рабочий код - рабочий.
Твой месседж я не понял к сожалению.
Приложи условие задачи и объясни что именно в решении тебе кажется противоречивым и почему ты считаешь свой подход (тоже приложи) более правильным.
Единственное что меня смущает здесь так это дублирующийся запуск тредов, в конструкторе TestThread и в мейн методе, но я этот код не разбирал, потому может так и надо.
0
Денис Кокшаров
5 марта 2025, 11:14
условие задачи:
Разобраться, что делает программа.
Почитать про UncaughtExceptionHandler - это важно.
Еще раз внимательно посмотреть программу.
Разобраться - продебажить - почему наш OurUncaughtExceptionHandler не срабатывает.
Исправить ошибку, т.е. все должно работать. :)
Ожидаемый результат в произвольном порядке:
Нить 1: My exception message
Нить 2: My exception message
Требования:
• Метод main должен создавать нить с параметрами: commonThread и "Нить 1".
• Метод main должен создавать нить с параметрами: commonThread и "Нить 2".
• Метод main должен запускать две созданные нити типа Thread.
• Метод main должен прерывать две созданные нити типа Thread.
• Программа с помощью метода uncaughtException класса OurUncaughtExceptionHandler должна вывести 2 сообщения.
• Метод uncaughtException класса OurUncaughtExceptionHandler явно не вызывать.
• Вывод программы должен содержать строки: "Нить 1: My exception message" и "Нить 2: My exception message".
0
Денис Enterprise Java Developer
5 марта 2025, 11:18
Ну из того что я вижу - результат достигнут. В чем конкретно твой поинт?
0
Денис Кокшаров
5 марта 2025, 11:25
противоречие вижу в:
в создании класса TestedThread ибо он используется для создания потока, хотя конструктор создания потоков принимает Runneble, а это все-таки реализация функционального интерфейса, а не класса наследуемого от Thread (узнал об этом из ругани IDEA), хотя компилируется и ошибки выполнения программы тоже не возникает.
ОК.
значит небольшой читинг в целом допустим, но зачем к нему прибегать, если в дальнейшем функционал, который мы за счет него получаем - не применяется.
т.е. чтобы установить обработчик так
доп класс не нужен, а если уж он есть, то в конструкторе можно было б сразу
setDefaultобработчик утсановить. тем более что обработчик ординаковый
+1
Денис Enterprise Java Developer
5 марта 2025, 11:42
Как минимум у класса Thread есть много разных конструкторов, никто не обязывает тебя пользоваться только одним конкретным.
Установка дефолтного обработчика может затронуть други потоки, в твоём случае тебя интересуют два конкретных.
0
Денис Кокшаров
5 марта 2025, 12:36
Единственное что меня смущает здесь так это дублирующийся запуск тредов, в конструкторе TestThread и в мейн методе, но я этот код не разбирал, потому может так и надо.
ну вот и в соотвествии с таким запуском логичнее было бы дефолтный обработчик установить в классе TestedThread
0
Денис Кокшаров
5 марта 2025, 12:43
" Установка дефолтного обработчика может затронуть други потоки, в твоём случае тебя интересуют два конкретных. "
вот теперь начинаю понимать, но тогда для чего отдельный класс тут заводится наследуемый от Thread, а не имплиментирующий Runnable?
0
Денис Enterprise Java Developer
5 марта 2025, 12:56полезный
чтобы понять почему надо копать ) По большому счёту это один из возможных вариантов, так почему он у тебя вызывает вопросы?) делать можно по разному.
+1
hidden #3303140
5 марта 2025, 12:56
Ты хорошо понимаешь, в чём разница между Runnable и Thread? У Runnable нет никакого хэндлера, Runnable само по себе не является тредом. Runnable - это просто исполняемое нечто, у чего гарантированно есть только метод run(). Тред - это в свою очередь обвязка над Runnable, которая расширяет его наличием имени, флага прерванности, обработчика исключений и чего ещё там только нет. Ты можешь создать свой тред, используя Runnable, но если ты просто создал какой раннабл - это не значит, что ты создал тред.
0
Денис Кокшаров
5 марта 2025, 14:39
Ты хорошо понимаешь, в чём разница между Runnable и Thread? У Runnable нет никакого хэндлера, Runnable само по себе не является тредом. Runnable - это просто исполняемое нечто, у чего гарантированно есть только метод run(). Тред - это в свою очередь обвязка над Runnable, которая расширяет его наличием имени, флага прерванности, обработчика исключений и чего ещё там только нет. Ты можешь создать свой тред, используя Runnable, но если ты просто создал какой раннабл - это не значит, что ты создал тред.
это я понимаю. речь как раз о том, что создание треда происходит с аргументом Thread, вместо Runnable, вот тут и вопрос, зачем так делать?
и вопрос я задаю именно потому что не понимаю для чего так сделано
0
Денис Кокшаров
5 марта 2025, 14:41
чтобы понять почему надо копать ) По большому счёту это один из возможных вариантов, так почему он у тебя вызывает вопросы?) делать можно по разному.
понимаю, что делать можно по разному, но в данном случае вопрос:
создание треда происходит с аргументом Thread, вместо Runnable, вот тут и вопрос, зачем так делать?
я вот не понимаю этого, это что-то важное или что-то неправильное или просто пофиг и идём дальше?
0
hidden #3303140
5 марта 2025, 16:11решение
Так, я наконец понял, о чём ты говоришь.
Я с тобой согласен.
В приведённом в вопросе коде я вижу противоречие в том, что общему потоку класса TestedThread устанавливают такой же обработчик исключений, как и потокам thread1 и thread2, при этом в TestedThread он никак не используется: его никто не прерывает, да и запускать его по сути не требовалось. В результате после прерывания нитей t1 и t2 нужно ещё ждать 3 секунды, пока нить commonThread закончит работу.
Поэтому тут стоило бы определиться с тем, какое предназначение класса TestedThread планировалось изначально. Если его использовать чисто как Runnable, то всё равно придётся вручную проставлять хэндлер внешним нитям, как у тебя и сделано. При этом его не требуется запускать при создании, и тогда класс TestedThread вообще не нужен. Можно было бы сделать просто так: Количество полезных действий не прибавилось, количество бесполезных убавилось.
Другой вариант - использовать конструктор TestedThread как неочевидное место установки дефолтного хэндлера. Это плохая практика, т.к. неочевидно и ведёт к возможным сайд-эффектам, о которых говорил Денис.
Третий вариант, если нужны действительно нити с предустановленным при создании хэндлером. Но опять-таки, в этом случае нет смысла создавать общую нить, а надо сами нити thread1 и thread2 делать объектами TestedThread, и пусть тогда конструктор только этой нити устанавливает этот хэндлер.
+4
hidden #3303140
5 марта 2025, 16:11
Короче да, я бы сказал не решение кривое, а само задание было кривое.
0
Денис Кокшаров
5 марта 2025, 16:58
господи. спасибо. я очень рад быть понятым, я очень рад новым знаниям. благодарю. всё было не зря, надеюсь еще кто-нибудь прочтёт это и тоже будет полезно.
+1
Денис Кокшаров
5 марта 2025, 17:04
но как раз не задание кривое, а реализация) потому что в задании только и написано, что понять что там написано и исправить, чтобы работало. но....
господи. я понял. эта кривизна им нужна была для того, чтобы вообще сделать это задание, иначе оно бы опять превратилось в EASY задачу, где нужно было подставить одно слово, а так пришлось вникнуть и разобраться и целую ветку комментов зафигарить.
0
hidden #3303140
5 марта 2025, 18:03
В задании поямо написано, что commonThread должно быть общей нитью, а это, получается, нелогично.
0
Денис Кокшаров
6 марта 2025, 04:25
верно
0