關於java中final關鍵字與執行緒安全性

來源:互聯網
上載者:User

標籤:也會   thread   cas   sso   hello   collect   lex   引用   需要   

在Java5中,final關鍵字是非常重要而事實上卻經常被忽視其作為同步的作用。本質上講,final能夠做出如下保證:當你建立一個對象時,使用final關鍵字能夠使得另一個線程不會訪問到處於“部分建立”的對象,否則是會可能發生的。這是 因為,當用作對象的一個屬性時,final有著如下的語義:

 當建構函式結束時,final類型的值是被保證其他線程訪問該對象時,它們的值是可見的

為什麼是必須的

使用final是所謂的安全發布(safe publication)的一種方式,這裡,發布(publication)一個地相意味著在一個線程中建立它,同時另一個線程在之後的某時刻可以引用到該新建立的對象。當JVM調用對象的建構函式時,它必須將各成員賦值,同時儲存一個指向該對象的指標。就像其他任何的資料寫入一樣,這可能是亂序的,and their application to main memory can be delayed and other processors can be delayed unless you take special steps to combat this(看不太懂,是不是說“把他們寫回主存可能延遲,並且其他的處理器(看到變化)也會延遲,要客服這一點,除非採取非常步驟”)。特別的,指向對象的引用可能在成員變數提交之前(導致如此的原因之一是編譯器的指令重排ordering:if you think about how you‘d write things in a low-level language such as C or assembler, it‘s quite natural to store a pointer to a block of memory, and then advance the pointer as you‘re writing data to that block)就被寫入到主存並被訪問到了。這樣會導致另一個線程看到了一個不合法或不完整的對象。

而final可以防止此類事情的發生:如果某個成員是final的,JVM規範做出如下明確的保證:一旦對象引用對其他線程可見,則其final成員也必須正確的賦值了。

final的對象引用

 對象的final成員成員的值在當退出建構函式時,他們也是最新的。這意味著:

 final類型的成員變數的值,包括那些用final引用指向的collections的對象,是讀安全執行緒而無需使用synchronization的

 注意,如果你有一個指向collection,數組或其他可變對象的final引用,如果存在其他線程訪問,仍然需要使用同步機制來訪問該對象(或使用ConcurrentHashMap)。

因此,不可變對象(指所有的成員都是final並且成員要麼是基本類型,要麼指向另一個不可變對象)可以並發訪問而無需使用同步機制。通過final引用讀取“實際不可變”對象(指成員雖然實際並不是final,然而卻從不會改變)也是安全的。然而,從程式設計的角度來看,在此種情況下強化不可變性是明智的(如用Collections.unmodifiableList()封裝一個collection)。That way, you‘ll spot bugs introduced when one of your colleagues naughtily attempts to modify a collection that you didn‘t intend to be modified!

 使用final的限制條件和局限性

 當聲明一個final成員時,必須在建構函式退出前設定它的值,如下:

public class MyClass {  private final int myField = 3;  public MyClass() {    ...  }}

或者

public class MyClass {  private final int myField;  public MyClass() {    ...    myField = 3;    ...  }}

需要強調的是將指向對象的成員聲明為final只能將該引用設為不可變的,而非所指的對象。例如如果一個list聲明如下:

private final List myList = new ArrayList();

仍然可以修改該list

myList.add("Hello");

然而,聲明為final可以保證如下操作不合法:

myList = new ArrayList();myList = someOtherList;

什麼時候應該使用final

 一個答案就是“儘可能的使用”。任何你不希望改變的(基本類型,或者指向一個對象,不管該對象是否可變)一般來講都應該聲明為final。另一種看待此問題的方式是:

 如果一個對象將會在多個線程中訪問並且你並沒有將其成員聲明為final,則必須提供其他方式保證安全執行緒

 “其他方式”可以包括聲明成員為volatile,使用synchronized或者顯式Lock控制所有該成員的訪問。

大家往往忽視的典型case是在一個線程建立一個對象,而後在另一個線程使用,如一個通過ThreadPoolExecutor的對象。這種情況下,必須保證該對象的執行緒安全性:這和線程的並發訪問關係不大,主要是因為在其生命週期內,不同的線程會在任意時刻訪問它(還是記憶體模型的問題吧)

關於java中final關鍵字與執行緒安全性

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.