首页 > 解决方案 > 如何让主线程等待完成 UncaughtExceptionHandler 执行

问题描述

我有以下多线程代码。我希望LatchCode.doStuff()等到UncaughtExceptionHandler处理程序完成它的工作,但事实并非如此。我怎么能让主线程等待它。对于某些项目要求,我需要将异常传播给父级,以将错误记录到数据库中(应该在处理结束时发生)。以下是一段代码。

public class LatchExceptionTest {
    public static void main(String[] args) {
        LatchCode l = new LatchCode();
        Cont c = new Cont();
        try {
            l.doStuff(c);
            System.out.println("Main Thread - work completed");
            if(!c.err.isEmpty())
                throw new Exception(c.err.toString());
        } catch (Exception e) {
            System.out.println("trace printing start");
            System.out.println(c.err.toString()); // log errors to DB
            System.out.println("trace printing edn");
        }
    }
}

class LatchCode {
    public void doStuff(final Cont cont) throws RuntimeException, InterruptedException {
        System.out.println("Intermediate class start");
        try {
            Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread th, Throwable ex) {
                    cont.err.add(ex.getMessage());
                }
            };
            Thread aggregatorThread = new Thread(() -> {
                try {
                    if(cont.err.size() > 0)
                        return;
                     System.out.println("AGGREGATOR thread START");
                     Thread.sleep(3000);
                     System.out.println("AGGREGATOR thread END");
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            CyclicBarrier barrier = new CyclicBarrier(2, aggregatorThread);
            AA a = new AA(); 
            BB b = new BB();  
            CountDownLatch latch = new CountDownLatch(2);
            Thread one = new Thread(() -> {
                try {
                    a.doSomething();
                } catch (Exception e) {
                    System.out.println("Exception in 1");
                    //Thread.currentThread().interrupt();
                    throw new RuntimeException(e.toString());
                } finally {
                    try {
                        barrier.await();
                    } catch (Exception e) {
                        System.out.println("Exception in 1 finallt");
                        throw new RuntimeException(e.toString());
                    } finally {
                        latch.countDown();
                    }
                }
            });
            Thread two = new Thread(() -> {
                try {
                   b.doSomething();
                } catch (Exception e) {
                    System.out.println("Exception in 2");
                    Thread.currentThread().interrupt();
                    throw new RuntimeException(e);
                } finally {
                    try {
                        barrier.await();
                    } catch (Exception e) {
                        System.out.println("Exception in 2 finallt");
                        throw new RuntimeException(e);
                    } finally {
                        latch.countDown();
                    }
                }
             });
            one.start();
            two.start();
            one.setUncaughtExceptionHandler(h);
            two.setUncaughtExceptionHandler(h);
            latch.await();
        } catch (Exception e) {
            System.out.println("Exception in caller");
            throw new RuntimeException(e);
        } finally {
            System.out.println("Intermediate class end");
        }
    }
}
class AA {
    public void doSomething() throws Exception {
        try {
            System.out.println("1 start");
            Thread.sleep(1); 
            throw new Exception("In AA");
        } catch (Exception e) {
            System.out.println("Exception in AA");
            throw new Exception(e.toString());
        }
    }
}
class BB {
    public void doSomething() throws Exception {
        try {
            System.out.println("2 start");
            Thread.sleep(1); 
        } catch (Exception e) {
            System.out.println("Exception in BB");
        }
        System.out.println("2 end");
    }
}
class Cont {
    ConcurrentLinkedQueue<String> err = new ConcurrentLinkedQueue<String>();
}

如果AA.doStuff()并且BB.doStuff()有记录器睡眠,那么我可以Cont.err不为空并进入 catch 块。但是如果睡眠时间可以忽略不计,比如 1 毫秒,那么如果阻塞main()失败并且程序正在执行,就好像没有异常一样。

所以我需要调用线程来等待UncaughtExceptionHandler完成。有人可以帮忙吗。

提前致谢

标签: javamultithreadingexceptioncountdownlatchuncaughtexceptionhandler

解决方案


经过详尽的搜索,找到了以下页面。详细了解 UEH 中的工作原理。 https://bugs.openjdk.java.net/browse/JDK-8153487

摘自上述主题的简短回答:

There is no guarantee that UncaughtExceptionHandlers have run before awaitTermination returns.
It is a pool thread that sets the state to TERMINATED, so it cannot wait for all pool threads to terminate!
It seems unlikely we can make this better. It seems that relying on the UEH in this way is a poor design

推荐阅读