[提问]关于主线程创建 N 多的线程,抛出'OOME: unable to create new native thread'error 后, catch 住 error,然后清理线程,此时主线程的表现问题。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
RuzZ
V2EX    Java

[提问]关于主线程创建 N 多的线程,抛出'OOME: unable to create new native thread'error 后, catch 住 error,然后清理线程,此时主线程的表现问题。

  •  1
     
  •   RuzZ 2019-05-23 18:49:11 +08:00 2850 次点击
    这是一个创建于 2333 天前的主题,其中的信息可能已经有所发展或是发生改变。

    运行环境:

    java version "1.8.0_191" Java(TM) SE Runtime Environment (build 1.8.0_191-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode) macOS 10.13.6 

    一个朋友问,线程可不可以从 OOM 中恢复,然后给了一篇网上看到的文章,大意是如果一个线程发生了 OOM,是不影响其他线程的正常运行的。

    然后我就想,如果主线程疯狂创建线程,也会抛出 OOM,那么如果我 catch 住异常,然后把创建的线程 stop 掉,是不是可行,于是写了下面一段代码测试。

    JVM 参数:-Xms256m -Xmx256m -Xss1m

    public class TestThread { public static void main(String[] args) throws InterruptedException { List<Thread> list = new ArrayList<>(); try { while (true) { Thread t = new Thread(() -> { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } }); list.add(t); t.start(); } } catch (Throwable e) { e.printStackTrace(); System.out.println("list size =================" + list.size()); System.out.println("thread active =================" + Thread.activeCount()); list.forEach(testThread -> { testThread.stop(); }); System.out.println("stop thread over"); } Thread.sleep(2000L); System.out.println("thread active =================" + Thread.activeCount()); Thread.currentThread().getThreadGroup().list(); } } 

    部分日志如下:

    Thread-2024 Thread-2025 Thread-2026 Thread-2027 java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:717) at com.xzy.test.TestThread.main(TestThread.java:25) list size =================2029 thread active =================2030 stop thread over thread active =================2 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Monitor Ctrl-Break,5,main] Process finished with exit code 0 

    可以看到,在 catch 住异常后,主线程正常执行了剩下的内容,正常退出了(exit code 0)。然后我就想,如果这里的是线程池呢?这个时候测试的出来的结果就有点不理解了。

    JVM 参数依然是:-Xms256m -Xmx256m -Xss1m

    public class TestThreadPool { public static void main(String[] args) throws InterruptedException { List<ExecutorService> list = new ArrayList<>(); try { while (true) { ExecutorService executorService = Executors.newFixedThreadPool(10); int i = 0; while (i++ <= 10) { executorService.submit(() -> {System.out.println(Thread.currentThread().getName());}); } list.add(executorService); } } catch (Throwable e) { System.out.println("catch throwable"); e.printStackTrace(); System.out.println("============" + Thread.currentThread().getName() + " collect thread start!"); System.out.println("list size ============" + list.size()); list.forEach(ExecutorService::shutdownNow); System.out.println("============" + Thread.currentThread().getName() + " shutdown success!"); } list.forEach(executorService -> { if (!executorService.isShutdown()) { System.out.println("============ alive executorservice"); } }); Thread.sleep(2000L); System.out.println("thread active count ============" + Thread.activeCount()); Thread.currentThread().getThreadGroup().list(); } } 

    部分日志输出如下:

    pool-203-thread-4 pool-203-thread-5 pool-203-thread-6 pool-203-thread-7 catch throwable java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:717) at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:957) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1367) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112) at com.xzy.test.TestThreadPool.main(TestThreadPool.java:20) ============main collect thread start! list size ============202 ============main shutdown success! thread active count ============9 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Monitor Ctrl-Break,5,main] Thread[pool-203-thread-1,5,main] Thread[pool-203-thread-2,5,main] Thread[pool-203-thread-3,5,main] Thread[pool-203-thread-4,5,main] Thread[pool-203-thread-5,5,main] Thread[pool-203-thread-6,5,main] Thread[pool-203-thread-7,5,main] 

    主线程没有退出,没理解错的话,因为pool-203线程池还在运行,但是为什么pool-203这个线程池会卡住呢,还是 catch 住异常之前的样子(都是新建了 7 个线程),我用 VisualVM 看了下线程的状态,发现pool-203-thread-这一批线程因为调用了java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#await()这个方法,都处于 waiting 状态。 visualVM 截图 看了眼 ThreadPoolExecutor,应该是java.util.concurrent.ThreadPoolExecutor#awaitTermination这里调用的,这个方法是有 shutdwon 请求时,阻塞等待所有任务完成执行。

    不知道我前面的分析有没有哪个地方有错。如果前面分析的没错的话,我的疑问是,为什么会卡在java.util.concurrent.ThreadPoolExecutor#awaitTermination这里呢,是因为创建线程过多发生了 oom,但是最后一组线程池没有正确创建完毕,导致主线程恢复过来后,无法彻底回收最后那组线程池吗?

    RuzZ
        1
    RuzZ  
    OP
       2019-05-23 18:50:03 +08:00
    不好意思,内容有点长,主要是自己也不知道怎么表达这个问题
    mononite
        2
    mononite  
       2019-05-23 23:09:00 +08:00
    list.add(executorService)是在 submit 后加的,最后抛异常的那个线程池就没有被加到 list 里,所以这个线程池没有被 shutdown。
    线程池默认创建的线程不是 Daemon 线程,如果 jvm 里没有退出的 Daemon 线程,jvm 是不会退出的。
    q253382683
        3
    q253382683  
       2019-05-24 09:00:38 +08:00
    gtmdryy
    vincentguc
        4
    vincentguc  
       2019-05-24 09:04:40 +08:00
    gtmdryy
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2724 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 08:35 PVG 16:35 LAX 01:35 JFK 04:35
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86