2 回答

TA貢獻(xiàn)1875條經(jīng)驗(yàn) 獲得超3個(gè)贊
executor 會(huì)發(fā)生什么(它是死線程的本地,沒有外部引用)?它應(yīng)該被GCed還是不被GCed?
答案比“是的,如果沒有引用它”更復(fù)雜。這取決于在 中運(yùn)行的線程ThreadPoolExecutor是否仍在運(yùn)行。這又取決于創(chuàng)建的 TPE 類型以及提交給它的“長(zhǎng)時(shí)間運(yùn)行的任務(wù)”是否已完成。
例如,如果任務(wù)尚未完成,則線程仍將運(yùn)行。即使它們已經(jīng)完成,如果您的 TPE 具有未設(shè)置的核心線程,allowCoreThreadTimeOut(true)那么線程也不會(huì)停止。JVM 從不垃圾收集正在運(yùn)行的線程,因?yàn)樗鼈儽徽J(rèn)為是GC“根”:
...根據(jù)定義,正在運(yùn)行的線程對(duì) GC 免疫。GC 通過掃描“根”開始其工作,這些“根”被認(rèn)為始終可以訪問;根包括全局變量(Java-talk 中的“靜態(tài)字段”)和所有正在運(yùn)行的線程的堆棧......
所以下一個(gè)問題是線程是否有引用回,ThreadPoolExecutor我相信他們有。內(nèi)部Worker類是Runnable存儲(chǔ)在其中并由它thread.target執(zhí)行的,Thread因此它不能被GC'd。 Worker并非static如此,它隱含了對(duì)外部ThreadPoolExecutor實(shí)例的引用。該run()方法實(shí)際上是調(diào)用ThreadPoolExecutor.runWorker()引用由ThreadPoolExecutor. 因此,正在運(yùn)行的線程保留對(duì)WorkerTPE 和 TPE 的引用,因此垃圾收集器無法收集 TPE。
例如,這是一個(gè)引用 TPE 的正在運(yùn)行的池線程的典型堆棧幀:
java.lang.Thread.sleep(Native Method)
com.j256.GcTester$1.run(GcTesteri.java:15)
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
>> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
>> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:748)
但是,如果線程池任務(wù)已全部完成并且它有 0 個(gè)核心線程或核心線程已超時(shí),那么將沒有Worker與ThreadPoolExecutor. 然后 TPE將被垃圾收集,因?yàn)槌?GC 足夠智能可以檢測(cè)到的循環(huán)引用之外,沒有對(duì)它的引用。
這是一個(gè)演示它的小示例測(cè)試程序。finalize()如果有 1 個(gè)核心線程,那么即使工作線程在注意到/tmp/x文件存在后退出,TPE 也永遠(yuǎn)不會(huì)關(guān)閉(通過)。即使主線程沒有引用它也是如此。但是,如果有 0 個(gè)核心線程,那么在線程超時(shí)后(這里是在完成最后一個(gè)任務(wù)后 1 秒后)TPE 將被收集。
public class GcTester {
public static void main(String[] args) {
int numCore = 1; // set to 0 to have it GC'd once /tmp/x file exists
ExecutorService pool =
new ThreadPoolExecutor(numCore, Integer.MAX_VALUE,
1, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()) {
protected void terminated() {
System.out.println(this + " terminated");
}
};
pool.submit(new Runnable() {
public void run() {
while (true) {
Thread.sleep(100); // need to handle exception here
if (new File("/tmp/x").exists()) {
System.out.println("thread exiting");
return;
}
}
}
});
pool = null; // allows it to be gc-able
while (true) {
Thread.sleep(1000); // need to handle exception here
System.gc(); // pull the big GC handle
}
}
}

TA貢獻(xiàn)1872條經(jīng)驗(yàn) 獲得超4個(gè)贊
線程就是所謂的GC 根。這意味著無法收集正在運(yùn)行(或未啟動(dòng))的線程。這也意味著無法收集從這些線程引用的對(duì)象,這就是為什么您可以執(zhí)行類似的操作new Thread(new MyRunnable()).start()
,或者在沒有任何引用的情況下運(yùn)行線程池。
如果線程是一個(gè)守護(hù)進(jìn)程,如果所有其他非守護(hù)線程都停止了,它可以自動(dòng)停止。您可以擁有帶有守護(hù)線程的線程池,但最好的辦法是確保正確清理事物(即異常不應(yīng)終止本應(yīng)最終停止并清理線程池的線程)。
添加回答
舉報(bào)