歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java多線程中易混淆的概念

Java多線程中易混淆的概念

日期:2017/3/1 9:32:33   编辑:Linux編程

概述

最近在看《ThinKing In Java》,看到多線程章節時覺得有一些概念比較容易混淆有必要總結一下,雖然都不是新的東西,不過還是蠻重要,很基本的,在開發或閱讀源碼中經常會遇到,在這裡就簡單的做個總結。

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是同步機制中最安全的一種方式,其他的任何方式都是有風險的,當然付出的代價也是最大的。

Copyright © Linux教程網 All Rights Reserved