引言
這題是一個網友@大臉貓愛吃魚
給我的提問,出自今年校招美團三面的一個真題。大致如下
一個進程有3個線程,如果一個線程拋出oom,其他兩個線程還能運行么?
先說一下答案,答案是還能運行
不瞞大家說,真在面試中,我遇到這一題,我估計也是答錯。因為我初看到這一題,內心嘿嘿一笑,覺得這題是在考察JVM的內存結構。我第一反應是OOM的常見情況堆溢出,也就是下面的這種異常
java.lang.OutOfMemoryError: Java heap space
先回憶一下,多線程中棧與堆是公有的還是私有的?回答如下
在多線程環境下,每個線程擁有一個棧和一個程序計數器。棧和程序計數器用來保存線程的執行歷史和線程的執行狀態,是線程私有的資源。其他的資源(比如堆、地址空間、全局變量)是由同一個進程內的多個線程共享。
也就是說,堆是線程共享。那么一個線程堆拋出OOM異常,我第一反應是另外兩個線程也拋出OOM異常,畢竟堆是共有的,大家應該都拋出異常。於是,我機智的讓@大臉貓愛吃魚
寫個代碼去測試一下,結果我被啪啪啪打臉了。
測試代碼偽如下
一個線程去構造堆溢出,每隔1秒申請一次堆,代碼長下面這樣
new Thread(() -> {
List<byte[]> list=new ArrayList<byte[]>();
while(true){
System.out.println(new Date().toString()+Thread.currentThread()+"==");
byte[] b = new byte[1024*1024*1];
list.add(b);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
另一個線程,睡眠1秒然后輸出就好,代碼長下面這樣
new Thread(() -> {
while(true){
System.out.println(new Date().toString()+Thread.currentThread()+"==");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
結果,輸出是長下面這樣的
大家發現了么,一個線程溢出了,其他線程還在跑,這好像和我們的認知不大一樣。坦白說,我看到這個結果,瞬間覺得自己一世英名毀於一旦,從此無法抬起頭來做人。沒辦法了,只能亮出工具來看一下了。
先說一下,在本例測試中,參數如下
-Xms16m -Xmx32m
-Xms 初始堆內存
-Xmx 最大堆內存
接下來,亮出JvisualVM
看堆的變化,注意看上面那張圖,拋出OOM的時間約在00:11:45
左右,因此我們需要重點關注00:11:45
左右的曲線變化,如下圖所示
如圖所示,我們仔細觀察一下在00:11:44
~00:11:45
之間曲線變化,你會發現使用堆的數量,突然間急劇下滑!這代表着一點,當一個線程拋出OOM異常后,它所占據的內存資源會全部被釋放掉,從而不會影響其他線程的運行!
講到這里大家應該懂了,此題的答案為一個線程溢出后,進程里的其他線程還能照常運行。注意了,這個例子我只演示了堆溢出的情況。如果是棧溢出,結論也是一樣的,大家可自行通過代碼測試。
說時遲,那時快。一個機智的網友又給我提了一個問題?
如果主線程拋異常退出了,子線程還能運行么?
ok,這個問題要從子線程和主線程的關系講起。
先來一個定義
線程不像進程,一個進程中的線程之間是沒有父子之分的,都是平級關系。即線程都是一樣的, 退出了一個不會影響另外一個。
因此,答案是如果主線程拋異常退出了,子線程還能運行。
但是有一個例外情況,如果這些子線程都是守護線程,那么子線程會隨着主線程結束而結束。