最近在公司寫需求時遇到了多線程與單例一同出現的狀況。安全
這個時候想到的就是線程安全以及單例的定義了,雖然單例指的是在內存中它只有一份,可是並非說就是線程安全的。多線程
因此,我當時就到網上找了關於多線程下單例的線程安全問題的資料,而後就知道以下博客:高併發下線程安全的單例模式(最全最經典)併發
其中,博主最推薦的寫做方式以下:高併發
public class MySingleton { //使用volatile關鍵字保其可見性 volatile private static MySingleton instance = null; private MySingleton(){} public static MySingleton getInstance() { try { if(instance != null){//懶漢式 }else{ //建立實例以前可能會有一些準備性的耗時工做 Thread.sleep(300); synchronized (MySingleton.class) { if(instance == null){//二次檢查 instance = new MySingleton(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
看了看內容確實是這個道理,而後就把這段代碼拿來使用了。而後在實際測試中發現,其並無保證線程安全的問題。測試
以後在同事的指點下發現,其實上文一段的線程安全僅僅只是在未實例化單例的前提下,以線程安全的方式實例化單例,使之在高併發多線程的環境下有且僅被new過一次。spa
也就是說,在單例被實例化以後,這段代碼是並無什麼做用的。.net
單例被實例化以後,instance != null一直成立,使getInstance()每次都是return instance。因此,多線程都能拿到指向同一個實例的引用。線程
因此即便是使用了這種雙檢查鎖機制的代碼,依然要對後面要使用到的公用方法作同步,以避免出現問題。code
而對公用方法作同步的操做也分兩種狀況。一種是公用方法裏只有局部變量,那麼此時不作同步也是能夠的,由於局部變量只會存在於相應的線程內存裏,並不會被其它線程所影響。另一種是含有成員變量,若是成員變量只有讀的操做,那不一樣步也能夠;若是成員變量涉及讀寫操做,那麼就要對相應的方法進行同步了。對象
局部變量不會受多線程影響
成員變量會受到多線程影響
多個線程應該是調用的同一個對象的同一個方法:
若是方法裏無成員變量,那麼不受任何影響
若是方法裏有成員變量,只有讀操做,不受影響
存在寫操做,考慮多線程影響值