1. 程式人生 > >多執行緒之volatile、ThreadLocal、synchronized關鍵字區別

多執行緒之volatile、ThreadLocal、synchronized關鍵字區別

     轉載自:https://blog.csdn.net/paincupid/article/details/47346423

1.volatile

volatile主要是用來在多執行緒中同步變數。 
在一般情況下,為了提升效能,每個執行緒在執行時都會將主記憶體中的變數儲存一份在自己的記憶體中作為變數副本,但是這樣就很容易出現多個執行緒中儲存的副本變數不一致,或與主記憶體的中的變數值不一致的情況。
而當一個變數被volatile修飾後,該變數就不能被快取到執行緒的記憶體中,它會告訴編譯器不要進行任何移出讀取和寫入操作的優化,換句話說就是不允許有不同於“主”記憶體區域的變數拷貝,所以當該變數有變化時,所有呼叫該變數的執行緒都會獲得相同的值,這就確保了該變數在應用中的可視性(當一個任務做出了修改在應用中必須是可視的),同時效能也相應的降低了(還是比synchronized高)。
但需要注意volatile只能確保操作的是同一塊記憶體,並不能保證操作的原子性。所以volatile一般用於宣告簡單型別變數,使得這些變數具有原子性,即一些簡單的賦值與返回操作將被確保不中斷。但是當該變數的值由自身的上一個決定時,volatile的作用就將失效,這是由volatile關鍵字的性質所決定的。
所以在volatile時一定要謹慎,千萬不要以為用volatile修飾後該變數的所有操作都是原子操作,不再需要synchronized關鍵字了。

2.ThreadLocal

首先ThreadLocal和本地執行緒沒有一毛錢關係,更不是一個特殊的Thread,它只是一個執行緒的區域性變數(其實就是一個Map),ThreadLocal會為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。這樣做其實就是以空間換時間的方式(與synchronized相反),以耗費記憶體為代價,單大大減少了執行緒同步(如synchronized)所帶來效能消耗以及減少了執行緒併發控制的複雜度。
個人覺得比較典型的例子就是在Android關於Looper的原始碼中對ThreadLocal的使用,同時也包含了ThreadLocal的基本用法,具體程式碼如下:
 

public class Looper { 
private static final String TAG = "Looper";
 
// sThreadLocal.get() will return null unless you've called prepare(). 
private static final ThreadLocal sThreadLocal = new ThreadLocal(); 
 
...... 
 
private static Looper mMainLooper = null; 
 
...... 
 
public static final void prepare() { 
  if (sThreadLocal.get() != null) { 
    throw new RuntimeException("Only one Looper may be created per thread"); 
  } 
  sThreadLocal.set(new Looper()); 
} 
 
...... 
 
public static final void prepareMainLooper() { 
  prepare(); 
  setMainLooper(myLooper()); 
 
  ...... 
 
} 
 
private synchronized static void setMainLooper(Looper looper) { 
  mMainLooper = looper; 
} 
public synchronized static final Looper getMainLooper() { 
  return mMainLooper; 
} 
 
...... 
 
public static final Looper myLooper() { 
  return (Looper)sThreadLocal.get(); 
} 
 
...... 
 
}


但需要注意的是,雖然ThreadLocal和Synchonized都用於解決多執行緒併發訪問,ThreadLocal與synchronized還是有本質的區別。synchronized是利用鎖的機制,使變數或程式碼塊在某一時該只能被一個執行緒訪問。而ThreadLocal為每一個執行緒都提供了變數的副本,使得每個執行緒在某一時間訪問到的並不是同一個物件,這樣就隔離了多個執行緒對資料的資料共享。而Synchronized卻正好相反,它用於在多個執行緒間通訊時能夠獲得資料共享。即Synchronized用於執行緒間的資料共享,而ThreadLocal則用於執行緒間的資料隔離。所以ThreadLocal並不能代替synchronized,Synchronized的功能範圍更廣(同步機制)。

3.synchronized

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