我有Java主类,在类中,我启动一个新线程,在主,它等待直到线程死亡。在某些时刻,我从线程中抛出一个运行时异常,但我无法捕获从主类中的线程抛出的异常。
以下是代码:
public class Test extends Thread
{
public static void main(String[] args) throws InterruptedException
{
Test t = new Test();
try
{
t.start();
t.join();
}
catch(RuntimeException e)
{
System.out.println("** RuntimeException from main");
}
System.out.println("Main stoped");
}
@Override
public void run()
{
try
{
while(true)
{
System.out.println("** Started");
sleep(2000);
throw new RuntimeException("exception from thread");
}
}
catch (RuntimeException e)
{
System.out.println("** RuntimeException from thread");
throw e;
}
catch (InterruptedException e)
{
}
}
}
有谁知道为什么?
答案 0 :(得分:190)
使用Thread.UncaughtExceptionHandler
。
Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread th, Throwable ex) {
System.out.println("Uncaught exception: " + ex);
}
};
Thread t = new Thread() {
public void run() {
System.out.println("Sleeping ...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted.");
}
System.out.println("Throwing exception ...");
throw new RuntimeException();
}
};
t.setUncaughtExceptionHandler(h);
t.start();
答案 1 :(得分:36)
这是因为异常是一个线程的本地,而你的主线程实际上并没有看到run
方法。我建议您阅读有关线程如何工作的更多信息,但要快速总结一下:您对start
的调用会启动一个与您的主线程完全无关的不同线程。对join
的调用只是等待它完成。在线程中抛出并且永远不会被捕获的异常终止它,这就是为什么join
在主线程上返回,但异常本身就丢失了。
如果你想了解这些未被捕获的例外,你可以试试这个:
Thread.setDefaultExceptionHandler(new UncaughtExceptionHandler() {
public void unchaughtException(Thread t, Throwable e) {
System.out.println("Caught " + e);
}
});
有关未捕获的异常处理的更多信息,请参见here。
答案 2 :(得分:36)
这解释了线程的状态转换,具体取决于是否发生异常:
答案 3 :(得分:17)
最有可能;
但是,假设您确实需要处理来自另一个子线程的异常。我会像这样使用ExecutorService:
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Void> future = executor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
System.out.println("** Started");
Thread.sleep(2000);
throw new IllegalStateException("exception from thread");
}
});
try {
future.get(); // raises ExecutionException for any uncaught exception in child
} catch (ExecutionException e) {
System.out.println("** RuntimeException from thread ");
e.getCause().printStackTrace(System.out);
}
executor.shutdown();
System.out.println("** Main stopped");
打印
** Started
** RuntimeException from thread
java.lang.IllegalStateException: exception from thread
at Main$1.call(Main.java:11)
at Main$1.call(Main.java:6)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
** Main stopped
答案 4 :(得分:5)
答案 5 :(得分:3)
使用Callable
而不是Thread,然后你可以调用Future#get()
,它会抛出Callable投掷的任何异常。
答案 6 :(得分:2)
目前,您只能抓取RuntimeException
,Exception
的子类。但是您的应用程序可能会抛出Exception的其他子类。除了Exception
RuntimeException
由于线程前端已经更改了许多内容,因此请使用高级java API。
首选java.util.concurrent API用于多线程,例如ExecutorService
或ThreadPoolExecutor
。
您可以自定义ThreadPoolExecutor来处理例外情况。
来自oracle文档页面的示例:
覆盖
protected void afterExecute(Runnable r,
Throwable t)
完成给定Runnable的执行后调用的方法。执行任务的线程调用此方法。如果非null,则Throwable是未被捕获的RuntimeException或Error,导致执行突然终止。
示例代码:
class ExtendedExecutor extends ThreadPoolExecutor {
// ...
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null)
System.out.println(t);
}
}
用法:
ExtendedExecutor service = new ExtendedExecutor();
我在上面的代码上添加了一个构造函数:
public ExtendedExecutor() {
super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
}
您可以更改此构造函数以满足您对线程数的要求。
ExtendedExecutor service = new ExtendedExecutor();
service.submit(<your Callable or Runnable implementation>);
答案 7 :(得分:2)
我遇到了同样的问题......很少解决(只针对实现而不是匿名对象)...我们可以将类级异常对象声明为null ...然后在catch块中为run方法初始化它。如果run方法中有错误,则此变量不为null ..然后我们可以对此特定变量进行null检查,如果它不为null,则在线程执行中有异常。
class TestClass implements Runnable{
private Exception ex;
@Override
public void run() {
try{
//business code
}catch(Exception e){
ex=e;
}
}
public void checkForException() throws Exception {
if (ex!= null) {
throw ex;
}
}
}
在join()
之后调用checkForException()答案 8 :(得分:1)
您是否使用过setDefaultUncaughtExceptionHandler()以及Thread类的相似方法?从API:“通过设置默认的未捕获异常处理程序,应用程序可以更改已经接受任何”默认“行为的线程处理未捕获异常的方式(例如,记录到特定设备或文件)系统提供。“
你可能会找到问题的答案......祝你好运! : - )
答案 9 :(得分:1)
也可以从Java 8中将Dan Cruz答案写为:
Thread t = new Thread(()->{
System.out.println("Sleeping ...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted.");
}
System.out.println("Throwing exception ...");
throw new RuntimeException(); });
t.setUncaughtExceptionHandler((th, ex)-> log(String.format("Exception in thread %d id: %s", th.getId(), ex)));
t.start();
答案 10 :(得分:0)
扩展Thread
几乎总是错误的。我不能强烈地说明这一点。
Thread
是错误的。* 如果您实施Runnable
,您将看到预期的行为。
public class Test implements Runnable {
public static void main(String[] args) {
Test t = new Test();
try {
new Thread(t).start();
} catch (RuntimeException e) {
System.out.println("** RuntimeException from main");
}
System.out.println("Main stoped");
}
@Override
public void run() {
try {
while (true) {
System.out.println("** Started");
Thread.sleep(2000);
throw new RuntimeException("exception from thread");
}
} catch (RuntimeException e) {
System.out.println("** RuntimeException from thread");
throw e;
} catch (InterruptedException e) {
}
}
}
产生
Main stoped
** Started
** RuntimeException from threadException in thread "Thread-0" java.lang.RuntimeException: exception from thread
at Test.run(Test.java:23)
at java.lang.Thread.run(Thread.java:619)
*除非您想要更改应用程序使用线程的方式,在99.9%的情况下,您不需要。如果您认为自己处于0.1%的情况,请参阅规则#1。
答案 11 :(得分:0)
如果在启动线程的类中实现Thread.UncaughtExceptionHandler,则可以设置然后重新抛出异常:
public final class ThreadStarter implements Thread.UncaughtExceptionHandler{
private volatile Throwable initException;
public void doSomeInit(){
Thread t = new Thread(){
@Override
public void run() {
throw new RuntimeException("UNCAUGHT");
}
};
t.setUncaughtExceptionHandler(this);
t.start();
t.join();
if (initException != null){
throw new RuntimeException(initException);
}
}
@Override
public void uncaughtException(Thread t, Throwable e) {
initException = e;
}
}
导致以下输出:
Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: UNCAUGHT
at com.gs.gss.ccsp.enrichments.ThreadStarter.doSomeInit(ThreadStarter.java:24)
at com.gs.gss.ccsp.enrichments.ThreadStarter.main(ThreadStarter.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.RuntimeException: UNCAUGHT
at com.gs.gss.ccsp.enrichments.ThreadStarter$1.run(ThreadStarter.java:15)
答案 12 :(得分:0)
Thread中的异常处理:默认情况下,run()方法不会抛出任何异常,因此必须在此处捕获并处理run方法中的所有已检查异常,对于运行时异常,我们可以使用UncaughtExceptionHandler。 UncaughtExceptionHandler是Java提供的用于处理Thread run方法中的异常的接口。因此,我们可以使用setUncaughtExceptionHandler()方法实现此接口并将实现类重新设置回Thread对象。但是这个处理程序必须在我们在踏板上调用start()之前设置。
如果我们不设置uncaughtExceptionHandler,则Threads ThreadGroup充当处理程序。
public class FirstThread extends Thread {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("FirstThread doing something urgent, count : "
+ (count++));
throw new RuntimeException();
}
}
public static void main(String[] args) {
FirstThread t1 = new FirstThread();
t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
System.out.printf("Exception thrown by %s with id : %d",
t.getName(), t.getId());
System.out.println("\n"+e.getClass());
}
});
t1.start();
}
}
给出了很好的解释
答案 13 :(得分:0)
我使用RxJava的解决方案:
@Test(expectedExceptions = TestException.class)
public void testGetNonexistentEntry() throws Exception
{
// using this to work around the limitation where the errors in onError (in subscribe method)
// cannot be thrown out to the main thread
AtomicReference<Exception> ex = new AtomicReference<>();
URI id = getRandomUri();
canonicalMedia.setId(id);
client.get(id.toString())
.subscribe(
m ->
fail("Should not be successful"),
e ->
ex.set(new TestException()));
for(int i = 0; i < 5; ++i)
{
if(ex.get() != null)
throw ex.get();
else
Thread.sleep(1000);
}
Assert.fail("Cannot find the exception to throw.");
}
答案 14 :(得分:0)
AtomicReference还是一种将错误传递给主线程的解决方案。与Dan Cruz的方法相同。
AtomicReference<Throwable> errorReference = new AtomicReference<>();
Thread thread = new Thread() {
public void run() {
throw new RuntimeException("TEST EXCEPTION");
}
};
thread.setUncaughtExceptionHandler((th, ex) -> {
errorReference.set(ex);
});
thread.start();
thread.join();
Throwable newThreadError= errorReference.get();
if (newThreadError!= null) {
throw newThreadError;
}
唯一的变化是,您可以使用AtomicReference而不是创建volatile变量,该变量在后台进行了相同的操作。
答案 15 :(得分:0)
对于需要停止所有线程运行并重新运行所有线程的人的用户:
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// could be any function
getStockHistory();
}
public void getStockHistory() {
// fill a list of symbol to be scrapped
List<String> symbolListNYSE = stockEntityRepository
.findByExchangeShortNameOnlySymbol(ContextRefreshExecutor.NYSE);
storeSymbolList(symbolListNYSE, ContextRefreshExecutor.NYSE);
}
private void storeSymbolList(List<String> symbolList, String exchange) {
int total = symbolList.size();
// I create a list of Thread
List<Thread> listThread = new ArrayList<Thread>();
// For each 1000 element of my scrapping ticker list I create a new Thread
for (int i = 0; i <= total; i += 1000) {
int l = i;
Thread t1 = new Thread() {
public void run() {
// just a service that store in DB my ticker list
storingService.getAndStoreStockPrice(symbolList, l, 1000,
MULTIPLE_STOCK_FILL, exchange);
}
};
Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable exception) {
// stop thread if still running
thread.interrupt();
// go over every thread running and stop every one of them
listThread.stream().forEach(tread -> tread.interrupt());
// relaunch all the Thread via the main function
getStockHistory();
}
};
t1.start();
t1.setUncaughtExceptionHandler(h);
listThread.add(t1);
}
}
总结:
您有一个创建多个线程的主函数,每个函数都有UncaughtExceptionHandler,该异常可由线程内部的任何Exception触发。您将每个线程添加到列表。如果触发了UncaughtExceptionHandler,它将在列表中循环,停止每个线程,然后重新启动主要函数以重新创建所有线程。
答案 16 :(得分:-5)
你不能这样做,因为它没有意义。如果您没有调用t.join()
,那么当t
线程抛出异常时,主线程可能位于代码中的任何位置。