2011-11-14 70 views
1

據我瞭解,Java沒有真正的關閉。你可以通過chaperoning把他們的功能傳遞給一個類;然而,不僅它是冗長的,而且(由於Java的內存模型)匿名類中的任何引用都被定義在構造環境中定義的變量時,它們被傳遞爲副本。該語言鼓勵我們記住這一點,只允許匿名課程參考final變量。匿名類中的非最終非局部變量

這使我這個代碼片斷我在Bloch的有效的Java發現:

import java.util.concurrent.*; 
public class StopThread { 
    private static boolean stopRequested; 

    public static void main(String[] args) 
        throws InterruptedException { 
     Thread backgroundThread = new Thread(new Runnable() { 
      public void run() { 
       int i = 0; 
       while (!stopRequested) 
        i++; 
      } 
     }); 
     backgroundThread.start(); 

     TimeUnit.SECONDS.sleep(1); 
     stopRequested = true; 
    } 
} 

首先,我希望編譯器抱怨,因爲stopRequested是非最終,我指的是匿名類中。我的編譯器沒有抱怨。其次,我期望程序能夠永久循環,因爲Java不支持閉包,並且如果匿名類實際上指的是它構建的環境中的實際變量(而不是簡單的拷貝),那麼我們似乎在這裏關閉了。 Joshua Bloch還表示,該程序在他的電腦上永遠循環播放。但是我的跑步一秒鐘後就退出了。

什麼部分的記憶模型是我誤解?

回答

3

你錯過了最關鍵的事情是匿名類是嵌套類。因此,它隱式引用了包含類的實例,因此也包含類的成員。

只有局部變量需要被final用於匿名類。

1

它爲我循環,原因是CPU緩存,而不是匿名方法。

有了這個它總是退出:

private volatile static boolean stopRequested; 
1

首先,我希望編譯器抱怨,因爲stopRequested是 非最終,我指的是匿名類中。我的編譯器 沒有抱怨。

stopRequestedstatic變量。

其次,我希望該程序永遠循環下去,因爲,好了,Java的 不支持關閉,如果匿名類確實是 指從它 構建環境實際stopRequested變量(而不是一個簡單的副本),那麼看起來我們在這裏有一個關閉 。 Joshua Bloch還表示,該程序永遠在他的電腦上循環播放。但是我的運行約一秒鐘,退出

stopRequested不是volatile變量。因此,它可能會永久運行(標誌吊裝優化。(以-server模式運行))。

因此,下面的代碼

 while (!stopRequested)  
     i++; 

可以重新作爲

boolean status = !stopRequested; 
    while(status)  
     i++; 
1

你誤解的是,只有局部變量作爲副本傳遞給內部類,因爲它們存在於堆棧中,因此在方法調用返回時會消失。

真正的關閉「神奇地」爲所有捕獲的變量生存提供了一個上下文。但是對於非局部變量,這並不是真的必要;它們作爲它們的對象或類的一部分存在於堆上,所以Java允許它們是非最終的並且仍然用在內部類中。不同的線程可能在其CPU緩存中有任何變量(本地,實例或靜態)的本地副本,並且一個線程所做的更改可能會有所不同其他線程無法長時間顯示。爲確保本地副本同步,更改要麼發生在​​塊/方法中,要麼必須將變量聲明爲volatile

+0

你絕對正確。布洛赫試圖通過一個不相關的概念,但引起我注意的是「什麼?Java已經關閉了???從什麼時候???」 感謝您的支持。那麼看起來Java幾乎已經關閉了?匿名類是從類或對象構造的閉包,但不是在構造函數時閉包? – achow

+0

@ highwind7777:匿名或本地類總是表現相同,幾乎關閉。從上下文來看,局部變量或實例或靜態變量的行爲是不同的:局部變量被複制,因此必須是最終的。實例變量通過對其封閉對象的隱藏引用進行訪問。 –

0

您可以通過引用(隱含地訪問OuterClass.this)或靜態字段按類訪問字段。這只是非最終變量,你不能引用。注意:如果這個最後的參考指向可變的東西,你可以改變它。

final int[] i = { 0 }; 
new Thread(new Runnable() { 
    public void run() { 
     i[0] = 1; 
    } 
}).start(); 
while(i[0] == 0); 
System.out.println("i= " + i[0]); 
0

stopRequested不是一個局部變量它是一個靜態變量,所以它不需要決賽。該程序可能會永久循環,因爲stopRequested未聲明爲volatile,因此無法保證一個線程所做的對stopRequested的更改會在另一個線程中看到。如果您聲明stopRequestedvolatile該程序不會永遠運行。

期待編譯器在這個例子中抱怨是不尋常的。通常的預期是,該計劃將在開始後立即終止。布洛赫表明,情況可能並非如此(通常令讀者驚訝),然後解釋原因。布洛赫是一個相當先進的閱讀,你可能想先嚐試其他關於Java的書籍。

相關問題