2012-07-26 78 views
1

我已經改變了文字,所以一些評論可能會提到以前的版本如何處理一對相關的線程對象?

以下是代碼示例。有兩個線程:observer和observable。可觀察是由main啓動的。觀察者從可觀察的對象創建開始,並打算結束它的銷燬。但它不會發生,觀察者永遠在運行。爲什麼?

public class ThreadObjectReaping01 { 

private static final Logger log = LoggerFactory.getLogger(ThreadObjectReaping01.class); 

public static void main(String[] args) throws InterruptedException { 

    Thread observable = new Thread("observable") { 

     private Thread observable2 = this; 

     Thread observer = new Thread("observer") { 
      @Override 
      public void run() { 

       log.info("Observer is starting"); 

       while(!interrupted()) { 

        if(observable2.isAlive()) { 
         log.info("Observable is running"); 
        } 
        else { 
         log.info("Observable is NOT running"); 
        } 

        try { 
         sleep(1000); 
        } catch (InterruptedException e) { 
         interrupt(); 
        } 
       } 

       log.info("Observer is terminating"); 

      } 
     }; 

     { 
      observer.start(); 
     } 

     @Override 
     protected void finalize() throws Throwable { 
      observer.interrupt(); 
     } 

     @Override 
     public void run() { 

      log.info("Observable is starting"); 

      while(!interrupted()) { 
       try { 
        sleep(1000); 
       } catch (InterruptedException e) { 
        interrupt(); 
       } 
       //log.info("Observable is running"); 
      } 


     } 
    }; 

    log.info("Main is starting observable"); 
    observable.start(); 
    Thread.sleep(10000); 
    log.info("Main is interrupting observable"); 
    observable.interrupt(); 
    observable = null; 
    Thread.sleep(10000); 
    log.info("Main is terminating"); 

} 
} 
+0

@John如何編碼不同?我想要丟棄物體。 – Dims 2012-09-10 15:22:11

回答

2

你的主要對象(一個覆蓋finalize())的那一刻不再可達(符合垃圾收集),GC將調用finalize()第一。我看到你正在跟蹤它,所以確保你的日誌消息實際上被稱爲。

然而,僅僅調用interrupt()是不夠的,守護線程必須主動檢查該標誌(使用isInterrupted())並正確響應,儘快關閉。如果有的話,你也應該正確處理InterruptedException

事實上,你的線程是守護線程是無關緊要的。非守護線程阻止JVM退出(當所有非守護線程完成其工作時JVM都存在)。在這裏你手動中斷線程 - 這個線程是否守護進程並不重要。

這只是一個垃圾收集器延遲或線程將永遠不會結束,因爲主對象不會被刪除,因爲它被記者引用?

您的"Reporting of..."消息是否曾經顯示?你可以在那裏放置一個斷點。這意味着執行finalize()的對象是GC的受害者。如果只有reporter線程持有對主對象的引用(反之亦然),GC仍然會釋放這些對象,如果它們都不是外部引用的,儘管存在循環依賴關係。

參見:

郵政Scriptum

還沒有相關的對你的問題。如果您正在使用,這樣的:

log.info(String.format("Reporting of (%s) is about ot interrupt", getName())); 

可以被替換爲:

log.info("Reporting of {} is about to interrupt", getName()); 
+0

不,我的報告消息沒有顯示,意味着finalize()不會被調用。這是個問題!由於它是內部類,因此除了來自記者對象的隱藏引用外,我沒有對主線程對象的引用。 – Dims 2012-08-06 13:51:10

2

我注意到,那個記者線程不結束,儘管它是在主線程編碼

線程在由於異常或返回而完成run()方法時結束。如果你正在談論thread.interrupt()那麼這只是設置中斷標誌,並導致一些方法(睡眠,等待......)拋出InterruptedException。在你的線程,你需要測試中斷標誌:

while (!Thread.currentThread().isInterrupted()) { 
    // here's how you should be catching InterruptedException 
    try { 
     ... 
    } catch (InterruptedException e) { 
     // re-enable the interrupted flag 
     Thread.currentThread.interrupt(); 
     // probably you should quit the thread too 
     return; 
    } 
} 

線程將被垃圾收集器一旦完成沒有人給Thread對象本身的引用來收穫。如果main仍然有一個Thread thread字段,那麼它永遠不會被垃圾收集。

順便說一句,使用finalize真的不鼓勵 - 特別是如果你正在登錄和在那裏做其他事情。你應該有自己的destroy()方法或一些這樣的線程完成時進行那種清理。也許是這樣的:

public void run() { 
    try { 
     // do the thread stuff 
    } finally { 
     destroy(); 
    } 
} 
+0

你確定它不應該是線程對象的引用嗎?對於其他對象,這不是真的:如果對象有相互引用,它們仍然是垃圾收集。 – Dims 2012-08-06 13:48:25

+0

P.S.因爲我沒有地方叫它,所以我不能擁有自己的'destroy()'。我有主線程對象,我可以不時調用,然後忘記它們。目前他們應該收集垃圾並且應該停止相關的記者。 – Dims 2012-08-06 13:54:17

+0

你可能會考慮不會忘記它們。分叉時可能會保留一組線程,以便清理它們。 – Gray 2012-08-06 18:25:40