java學習(十四)-volatile、ThreadLocal與synchronized

1.volatile
volatile主要是用來在多線程中同步變量。
在一般情況下,爲了提升性能,每個線程在運行時都會將主內存中的變量保存一份在自己的內存中作爲變量副本,但是這樣就很容易出現多個線程中保存的副本變量不一致,或與主內存的中的變量值不一致的情況。

  • 立即可見性:而當一個變量被volatile修飾後,該變量就不能被緩存到線程的內存中,它會告訴編譯器不要進行任何移出讀取和寫入操作的優化,換句話說就是不允許有不同於“主”內存區域的變量拷貝,所以當該變量有變化時,所有調用該變量的線程都會獲得相同的值,這就確保了該變量在應用中的立即可視性(當一個任務做出了修改在應用中必須是可視的),同時性能也相應的降低了(還是比synchronized高)。
  • 不能保證原子性:但需要注意volatile只能確保操作的是同一塊內存,並不能保證操作的原子性。所以volatile一般用於聲明簡單類型變量,使得這些變量具有原子性,即一些簡單的賦值與返回操作將被確保不中斷。但是當該變量的值由自身的上一個決定時,volatile的作用就將失效,這是由volatile關鍵字的性質所決定的。 所以在volatile時一定要謹慎,千萬不要以爲用volatile修飾後該變量的所有操作都是原子操作,不再需要synchronized關鍵字了。
  • 禁止指定重排序:虛擬機層面對我們轉換成二進制機器碼的代碼進行重新排序執行,使效率更高。爲了避免多線程下各個線程代碼執行的順序不一致,我們會使用volatile這樣的修飾去禁止指令的重新排序。

2.ThreadLocal
首先ThreadLocal和本地線程沒有一毛錢關係,更不是一個特殊的Thread,它只是一個線程的局部變量(其實就是一個Map),,並不是解決資源共享的問題,而是用來提供線程內的局部變量,這樣每個線程都自己管理自己的局部變量,別的線程操作的數據不會對我產生影響,互不影響,所以不存在解決資源共享這麼一說,如果是解決資源共享,那麼其它線程操作的結果必然我需要獲取到,而ThreadLocal則是自己管理自己的,相當於封裝在Thread內部了,供線程自己管理。
即ThreadLocal會爲每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。這樣做其實就是以空間換時間的方式(與synchronized相反),以耗費內存爲代價,單大大減少了線程同步(如synchronized)所帶來性能消耗以及減少了線程併發控制的複雜度。

  • 雖然ThreadLocal和Synchonized都用於解決多線程併發訪問,ThreadLocal與synchronized還是有本質的區別。
    • 數據訪問性:synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。,而ThreadLocal爲每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的並不是同一個對象,這樣就隔離了多個線程對數據的數據共享。
    • 數據隔離性:Synchronized卻正好相反,它用於在多個線程間通信時能夠獲得數據共享。即Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。所以ThreadLocal並不能代替synchronized,Synchronized的功能範圍更廣(同步機制)。

3.synchronized
synchronized關鍵字是Java利用鎖的機制自動實現的,一般有同步方法和同步代碼塊兩種使用方式。Java中所有的對象都自動含有單一的鎖(也稱爲監視器),當在對象上調用其任意的synchronized方法時,此對象被加鎖(一個任務可以多次獲得對象的鎖,計數會遞增),同時在線程從該方法返回之前,該對象內其他所有要調用類中被標記爲synchronized的方法的線程都會被阻塞。當然針對每個類也有一個鎖(作爲類的Class對象的一部分),所以你懂的.
最後需要注意的是synchronized是同步機制中最安全的一種方式,其他的任何方式都是有風險的,當然付出的代價也是最大的。

參考
轉載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章