Есть пул потоков (newCachedThreadPool). Ему сбрасывается задание через ExecutorService.submit. В этом задании возникает exception (какой именно ИМХО не важно, но все же NPE).
Когда ExecutorService вызывает get, он получает ExecutionException. Вроде как все правильно.
Вопрос же вот какой. Почему-то абсолютно отсутствует какая бы то ни было выдача по случаю самого NPE. То есть в потоке он произошел и ... ничего. Никакого stack trace в окне Console Eclipse, совершенно ничего. Не вызови я get (а я в дальнейшем хочу его и не вызывать, так как результат мне, вообще-то, не нужен) — я бы так ничего и не узнал.
Кто-то может что-нибудь сказать ?
Вот простейший пример
public class ThreadTest {
static class ThreadClass implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("Inside call");
Object o = null;
return o.hashCode();
}
}
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
Future<Integer> f = es.submit(new ThreadClass());
/* try {
Integer i = f.get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
}
}
В таком виде печатает "Inside call" и заканчивается молча.
Декомментируем — получаем exception.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Есть пул потоков (newCachedThreadPool). Ему сбрасывается задание через ExecutorService.submit. В этом задании возникает exception (какой именно ИМХО не важно, но все же NPE).
PD>Когда ExecutorService вызывает get, он получает ExecutionException. Вроде как все правильно.
PD>Вопрос же вот какой. Почему-то абсолютно отсутствует какая бы то ни было выдача по случаю самого NPE. То есть в потоке он произошел и ... ничего. Никакого stack trace в окне Console Eclipse, совершенно ничего. Не вызови я get (а я в дальнейшем хочу его и не вызывать, так как результат мне, вообще-то, не нужен) — я бы так ничего и не узнал.
С точки зрения реализации — обработкой таких исключений занимается Thread.uncaughtExceptionHandler и вероятно ExecutorService устанавливает соотвтствующий handler (в сорцы не лазил).
С точки зрения логики — исключение в потоке ExecutorService это вполне нормальная ситуация, которую надо обрабатывать через методы возвращённого Future. Вы же не требуете что бы любое кинутое исключение выводилось в консоль? А то что вам не нужен результат и get можно не звать — так это неправда, вы же зачем-то таск в executor заслали, значит вам в какой-то момент времени станет важно что он уже выполнился. Тут-то вы исключение и словите, если оно произошло.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Вопрос же вот какой. Почему-то абсолютно отсутствует какая бы то ни было выдача по случаю самого NPE. То есть в потоке он произошел и ... ничего. Никакого stack trace в окне Console Eclipse, совершенно ничего. Не вызови я get (а я в дальнейшем хочу его и не вызывать, так как результат мне, вообще-то, не нужен) — я бы так ничего и не узнал.
А почему тогда Callable, а не Runnable? Да и можно собственно постоить своего наследника Runnable, у которого обёрнут метод run, с отловом исключений.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Вопрос же вот какой. Почему-то абсолютно отсутствует какая бы то ни было выдача по случаю самого NPE. То есть в потоке он произошел и ... ничего. Никакого stack trace в окне Console Eclipse, совершенно ничего. Не вызови я get (а я в дальнейшем хочу его и не вызывать, так как результат мне, вообще-то, не нужен) — я бы так ничего и не узнал. PD>Кто-то может что-нибудь сказать ?
Если хотите логировать, то вам никто не запрещает — оберните call в try/catch. Не совсем понятно, что вы от джавы хотите? Логировать это в System.err? Как бы не все хотят того же.
Здравствуйте, PAS_Tor, Вы писали:
PD>>Вопрос же вот какой. Почему-то абсолютно отсутствует какая бы то ни было выдача по случаю самого NPE. То есть в потоке он произошел и ... ничего. Никакого stack trace в окне Console Eclipse, совершенно ничего. Не вызови я get (а я в дальнейшем хочу его и не вызывать, так как результат мне, вообще-то, не нужен) — я бы так ничего и не узнал.
PAS>А почему тогда Callable, а не Runnable? Да и можно собственно постоить своего наследника Runnable, у которого обёрнут метод run, с отловом исключений.
Скорее всего в конечном счете будет именно Runnable, но пока Callable, так как на результат посмотреть хочется. Но он мне не важен — все равно ничего сделать нельзя по условиям задачи. Получилось — хорошо, нет — черт с ним. Но пока идет отладка, я хочу иметь диагностику, если там внутри call что-то не так.
Здравствуйте, StanislavK, Вы писали:
SK>Если хотите логировать, то вам никто не запрещает — оберните call в try/catch.
Вопрос же не в этом. Проблемы у меня нет, как отловить эти исключения я понимаю
>Не совсем понятно, что вы от джавы хотите? Логировать это в System.err? Как бы не все хотят того же.
Вот об этом , пожалуйста, подробнее. Почему все хотят (уж не знаю, хотят или нет, но по умолчанию делается) логгирование в основном потоке и почему нет — в потоке пула ?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Вот об этом , пожалуйста, подробнее. Почему все хотят (уж не знаю, хотят или нет, но по умолчанию делается) логгирование в основном потоке и почему нет — в потоке пула ?
Потому что это распараллеливание задачи и обычно все вызывают Future.get() чтоб дождаться результата выполнения всех запущенных задач. А это значит, что исключение к вам прийдёт и тут уж вы сами решите логировать, игнорить или еще что-нибудь с ним делать.
Здравствуйте, StanislavK, Вы писали:
SK>Если хотите логировать, то вам никто не запрещает — оберните call в try/catch.
Кстати, а это как ? Вот Call Stack этого call
Thread [pool-1-thread-1] (Suspended (breakpoint at line 14 in ThreadTest$ThreadClass))
ThreadTest$ThreadClass.call() line: 14
ThreadTest$ThreadClass.call() line: 1
FutureTask$Sync.innerRun() line: not available
FutureTask<V>.run() line: not available
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: not available
ThreadPoolExecutor$Worker.run() line: not available
Thread.run() line: not available
Куда мне со своим try-catch, в FutureTask$Sync.innerRun ?
Здравствуйте, PAS_Tor, Вы писали:
PAS>Потому что это распараллеливание задачи и обычно все вызывают Future.get() чтоб дождаться результата выполнения всех запущенных задач. А это значит, что исключение к вам прийдёт и тут уж вы сами решите логировать, игнорить или еще что-нибудь с ним делать.
Ну давай без Future
public class ThreadTest {
static class ThreadClass implements Runnable{
@Override
public void run() {
System.out.println("Inside call");
Object o = null;
int hash = o.hashCode();
}
}
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
es.submit(new ThreadClass());
}
}
То же самое. И не придет, естественно — некуда ему придти.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, PAS_Tor, Вы писали:
PAS>>Потому что это распараллеливание задачи и обычно все вызывают Future.get() чтоб дождаться результата выполнения всех запущенных задач. А это значит, что исключение к вам прийдёт и тут уж вы сами решите логировать, игнорить или еще что-нибудь с ним делать.
PD>Ну давай без Future
Без Future приложение никак не сможет узнать, что произошло — выкинулось NPE или планировщик решил что с потока хватит и просто не даёт ему квант времени что бы этот NPE произошёл (я слегка утрирую, при большом желании по косвенным признакам узнать можно). А раз самому приложению на это наплевать, то почему вам не всё равно?
Не написать проверку Future это примерно то же самое, что написать пустой catch(Throwable t) {} и потом удивляться, почему в консоли ничего нет. Я понимаю причины вашего недомения, но всё же существующее решение продиктовано вполне объективными причинами и работает хорошо в большинстве ситуаций. Если вам по какой-то причине хочется иного поведения — можете расширить ExecutorService переопределив newTaskFor и подсунуть туда FutureTask который будет в done() писать исключения в консоль.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Почему все хотят (уж не знаю, хотят или нет, но по умолчанию делается) логгирование в основном потоке и почему нет — в потоке пула ?
Если поток нигде не перехватывает исключение, то он завершается, а исключение логируется в System.err неким дефолтным хэндлером.
Executor-ы пере-используют потоки. Поэтому когда они перехватывают исключение, они его себе запоминают. Но дальше не прокидывают чтобы не убить поток.
Здравствуйте, RomikT, Вы писали:
RT>Без Future приложение никак не сможет узнать, что произошло — выкинулось NPE или планировщик решил что с потока хватит и просто не даёт ему квант времени что бы этот NPE произошёл
Во-первых, ни о каком приложении речь идти не может, а только о запускающем потоке. Он, точно, не сможет узнать для случая с Runnable. Но я этого и не просил.
Во-вторых, планировщик никак не может решить, что с потока хватит и не давать ему больше времени. NPE возникает как результат страничного исключения , перехватываемого ядром ОС и портируемого в java vm. Собственно, когда уже NPE, исключения в ОС больше нет,оно обработано java vm, а дальше идет нормальное выполнение с возможным убиением потока потом.
>(я слегка утрирую, при большом желании по косвенным признакам узнать можно). А раз самому приложению на это наплевать, то почему вам не всё равно?
Думал над этой фразой и пытался понять, но так и не смог. Судя по всему, есть некое самозародившееся приложение, которому на это наплевать, а есть я, который при сем присутсвует, и на это все смотрит.
RT>Не написать проверку Future это примерно то же самое, что написать пустой catch(Throwable t)
Нет там и не может быть никакого Future, поскольку в моем последнем примере не Callable, а Runnable.
>{} и потом удивляться, почему в консоли ничего нет. Я понимаю причины вашего недомения, но всё же существующее решение продиктовано вполне объективными причинами и работает хорошо в большинстве ситуаций. Если вам по какой-то причине хочется иного поведения — можете расширить ExecutorService переопределив newTaskFor и подсунуть туда FutureTask который будет в done() писать исключения в консоль.
Здравствуйте, Blazkowicz, Вы писали:
PD>>Почему все хотят (уж не знаю, хотят или нет, но по умолчанию делается) логгирование в основном потоке и почему нет — в потоке пула ? B>Если поток нигде не перехватывает исключение, то он завершается, а исключение логируется в System.err неким дефолтным хэндлером. B>Executor-ы пере-используют потоки. Поэтому когда они перехватывают исключение, они его себе запоминают. Но дальше не прокидывают чтобы не убить поток.
Для нативных (Win32) потоков, если в потоке произошло необработанное исключение, то его состояние труднопредсказуемо, поэтому его убивают. Замечу, не ядро убивает, а библиотека исполняющей системы (С++ к примеру). Ты хочешь сказать, что java vm настолько уверена в себе, что готова передать заново управление потоку, в котором было необработанное программистом исключение? Понятно, что java vm его обработала, но брать на себя обработку ЛЮБЫХ исключений и быть при этом уверенным, что поток останется в рабочем состоянии... однако...
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Для нативных (Win32) потоков, если в потоке произошло необработанное исключение, то его состояние труднопредсказуемо, поэтому его убивают. Замечу, не ядро убивает, а библиотека исполняющей системы (С++ к примеру).
Какое это имеет отношения к Java. Сама JVM, естественно, исключение перехватывает. Ведь оно логируется в консоль, а не приводит к крашу процесса.
PD>Ты хочешь сказать, что java vm настолько уверена в себе, что готова передать заново управление потоку, в котором было необработанное программистом исключение?
Чего? Исключение было обработано в реализации Executor Service. Почему оно у тебя "необработанное"?
PD>Понятно, что java vm его обработала,
При чем здесь JVM? По твоей логике если не дай бог в блоке try...catch исключение попало в catch, то поток нужно завершать?
PD>но брать на себя обработку ЛЮБЫХ исключений и быть при этом уверенным, что поток останется в рабочем состоянии... однако...
А что с ним станется, с потоком-то???
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, RomikT, Вы писали:
RT>>Без Future приложение никак не сможет узнать, что произошло — выкинулось NPE или планировщик решил что с потока хватит и просто не даёт ему квант времени что бы этот NPE произошёл
PD>Во-первых, ни о каком приложении речь идти не может, а только о запускающем потоке. Он, точно, не сможет узнать для случая с Runnable.
ExecutorService.submit(Runnable) тоже возвращает Future. PD>Во-вторых, планировщик никак не может решить, что с потока хватит и не давать ему больше времени.
Когда-нибудь он конечно квант времени выделит, просто неизвестно когда.
>>(я слегка утрирую, при большом желании по косвенным признакам узнать можно). А раз самому приложению на это наплевать, то почему вам не всё равно? PD>Думал над этой фразой и пытался понять, но так и не смог. Судя по всему, есть некое самозародившееся приложение, которому на это наплевать, а есть я, который при сем присутсвует, и на это все смотрит.
Eсть приложение в котором основной поток шлёт задачи в ExecutorService, но совершенно не интересуется их судьбой. В частности, ему не важно успели они выполниться или нет (вызова Future.get нет). Из этого я делаю логичный вывод что ему точно так же не может быть важно по какой причине таски не выполнились — потому что не успели или потому что исключение вылетело.
Здравствуйте, RomikT, Вы писали:
PD>>Во-первых, ни о каком приложении речь идти не может, а только о запускающем потоке. Он, точно, не сможет узнать для случая с Runnable. RT>ExecutorService.submit(Runnable) тоже возвращает Future.
Да, верно. Только get из него вернет null. Впрочем, да, конечно, дождались хотя бы.
PD>>Во-вторых, планировщик никак не может решить, что с потока хватит и не давать ему больше времени. RT>Когда-нибудь он конечно квант времени выделит, просто неизвестно когда.
А какое это имеет значение ? Я же не квант ждал
PD>>Думал над этой фразой и пытался понять, но так и не смог. Судя по всему, есть некое самозародившееся приложение, которому на это наплевать, а есть я, который при сем присутсвует, и на это все смотрит. RT>Eсть приложение в котором основной поток шлёт задачи в ExecutorService, но совершенно не интересуется их судьбой. В частности, ему не важно успели они выполниться или нет (вызова Future.get нет). Из этого я делаю логичный вывод что ему точно так же не может быть важно по какой причине таски не выполнились — потому что не успели или потому что исключение вылетело.
Вот тут неверно ИМХО. У меня именно такая ситуация, но я согласен, если задача не выполнится, если, скажем, обрыв связи и т.п, но меня совсем не устраивает NPE в задаче. Это моя ошибка, я хочу о ней диагностику.
В общем, здесь ИМХО не вполне корректный дизайн. Должен быть флаг, указывающий, как вести себя ExecutorService в таких случаях. Для отладки true, в production false.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>В общем, здесь ИМХО не вполне корректный дизайн. Должен быть флаг, указывающий, как вести себя ExecutorService в таких случаях. Для отладки true, в production false.
Не должен. Это не ответственность библиотечной службы выполнения задач. Да и то, у ScheduledThreadPoolExecutor можно decorateTask переопределить и делать там, что нужно. А правильнее сделать свою реализацию ExecutorService, которая ваши Runnable оборачивает в нужные вам логгеры (или что там вы хотите) и передает в следующий ExecutorService. При отладке использовать ваш Executor, в production можно и без обертки. Хотя в последнем я смысла не вижу, если вдруг ошибка будет и в production, ее тоже стоит как-то обработать.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Куда мне со своим try-catch, в FutureTask$Sync.innerRun ?
Ничего сммешного. Создайте свой Future на основе FutureTask, переопределив run() и подавайте его на исполнение через Executor.exec(). Если хочется через ExecutorService.submit, сделайте свой ExecutorService, переопределив submit.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, RomikT, Вы писали:
RT>>Без Future приложение никак не сможет узнать, что произошло — выкинулось NPE или планировщик решил что с потока хватит и просто не даёт ему квант времени что бы этот NPE произошёл PD>Во-первых, ни о каком приложении речь идти не может, а только о запускающем потоке. Он, точно, не сможет узнать для случая с Runnable. Но я этого и не просил. PD>Во-вторых, планировщик никак не может решить, что с потока хватит и не давать ему больше времени. NPE возникает как результат страничного исключения , перехватываемого ядром ОС и портируемого в java vm. Собственно, когда уже NPE, исключения в ОС больше нет,оно обработано java vm, а дальше идет нормальное выполнение с возможным убиением потока потом. >>(я слегка утрирую, при большом желании по косвенным признакам узнать можно). А раз самому приложению на это наплевать, то почему вам не всё равно? PD>Думал над этой фразой и пытался понять, но так и не смог. Судя по всему, есть некое самозародившееся приложение, которому на это наплевать, а есть я, который при сем присутсвует, и на это все смотрит. RT>>Не написать проверку Future это примерно то же самое, что написать пустой catch(Throwable t) PD>Нет там и не может быть никакого Future, поскольку в моем последнем примере не Callable, а Runnable. >>{} и потом удивляться, почему в консоли ничего нет. Я понимаю причины вашего недомения, но всё же существующее решение продиктовано вполне объективными причинами и работает хорошо в большинстве ситуаций. Если вам по какой-то причине хочется иного поведения — можете расширить ExecutorService переопределив newTaskFor и подсунуть туда FutureTask который будет в done() писать исключения в консоль. PD>М-да...
Я вот вас опять не понимаю. Вроде серьезный мужчина, а троллите.
Есть аксиома — исключение в главное потоке приложения (который main) — логится, остальные — нет. Вы хотите обсудить почему оно так? Потому, что часть приложения так падают с исключением из главноего потока. Разработчикам джавы кажется, что так оно лучше будет, если его ложить (извиняюсь за французский).
Насчет остальных потоков — если у вас есть желание, то вы всегда можете поймать и залогировать, если, конечно, код, вызывающий исключение, вызывается вашим кодом.
Да, прочитав остальные сообщения в топике, смею напомнить, что java это не C++ и исключение там — нормальное дело, даже NPE.
Вот насчет этого: "NPE возникает как результат страничного исключения" я бы послушал поподробнее. Вы это откуда взяли?
Здравствуйте, StanislavK, Вы писали:
SK>Я вот вас опять не понимаю. Вроде серьезный мужчина, а троллите.
Я Вас тоже. Вроде серьезная дискуссия, а Вы прибегаете к таким аргументам...
SK>Есть аксиома — исключение в главное потоке приложения (который main) — логится, остальные — нет.
Вот я и хотел узнать, почему это так ? Слово аксиома меня не убеждает — мы не в аксиоматической системе.
>Вы хотите обсудить почему оно так?
Именно. Почему это так, если в нативном коде это не так (там, конечно, ничего не логгируется, но и просто так исключение не гасят (а могли бы).
SK>Насчет остальных потоков — если у вас есть желание, то вы всегда можете поймать и залогировать, если, конечно, код, вызывающий исключение, вызывается вашим кодом. SK>Да, прочитав остальные сообщения в топике, смею напомнить, что java это не C++ и исключение там — нормальное дело, даже NPE.
Отмечу, что и в С++ исключения — вполне нормальное дело и обрабатыаются практически так же.
SK>Вот насчет этого: "NPE возникает как результат страничного исключения" я бы послушал поподробнее. Вы это откуда взяли?
При обращении по null происходит обращение по нулевому адресу (null pointer exception). Такое обращение приводит к исключению в процессоре , которое называется page fault, потому что в ОС Windows адреса в интервале 0..64К-1 недоступны, то есть страничный механизм настроен так, чтобы обращения к любому из этих адресов генерировали исключение. Выяснив причину page fault (причины могут быть разные, не обязательно связанные с действиями программы, например, если просто страница вытеснена в своп — тоже будет page fault), операционная система передает исключение в прикладную программу, после чего прикладная программа (в данном случае java vm) обрабатывает это исключение процессора и генерирует свое, явовское исключение (в данном случае NPE, в другом случае, скажем, ArithmeticException), которое и передается программе для обработки.
Это стандартный механизм обработки исключений процесора в x86/x64 по крайней мере.