运行环境:
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 状态。
看了眼 ThreadPoolExecutor,应该是java.util.concurrent.ThreadPoolExecutor#awaitTermination
这里调用的,这个方法是有 shutdwon 请求时,阻塞等待所有任务完成执行。
不知道我前面的分析有没有哪个地方有错。如果前面分析的没错的话,我的疑问是,为什么会卡在java.util.concurrent.ThreadPoolExecutor#awaitTermination
这里呢,是因为创建线程过多发生了 oom,但是最后一组线程池没有正确创建完毕,导致主线程恢复过来后,无法彻底回收最后那组线程池吗?
1
RuzZ OP 不好意思,内容有点长,主要是自己也不知道怎么表达这个问题
|
2
mononite 2019-05-23 23:09:00 +08:00
list.add(executorService)是在 submit 后加的,最后抛异常的那个线程池就没有被加到 list 里,所以这个线程池没有被 shutdown。
线程池默认创建的线程不是 Daemon 线程,如果 jvm 里没有退出的 Daemon 线程,jvm 是不会退出的。 |
3
q253382683 2019-05-24 09:00:38 +08:00
gtmdryy
|
4
vincentguc 2019-05-24 09:04:40 +08:00
gtmdryy
|