【Java多執行緒】單例模式與多執行緒
阿新 • • 發佈:2019-01-29
單例模式大家都不陌生,即讓一個類只有一個例項。
單例模式分為懶漢式和餓漢式。
懶漢式☞方法呼叫時再例項化物件,什麼時候用什麼時候例項化,比較懶。
餓漢式☞方法呼叫前物件就已經建立好了,比較有捉急。
本文著重描述懶漢式與多執行緒的內容。
1.餓漢式
public class SingletonHungary { private static SingletonHungary instance = new SingletonHungary(); private SingletonHungary(){} public static SingletonHungary getInstance() { return instance; } }
public class Main extends Thread{ @Override public void run() { System.out.println(SingletonHungary.getInstance().hashCode()); } public static void main(String[] args) { Main[] mts = new Main[10]; for(int i = 0 ; i < mts.length ; i++){ mts[i] = new Main(); } for (int j = 0; j < mts.length; j++) { mts[j].start(); } } }
由打印出的hashcode相同說明:他們是同一個物件
餓漢式在方法呼叫之前就建立了靜態物件,靜態物件是全域性唯一的,即使有多個執行緒訪問也是在使用同一個物件。這樣的方式是執行緒不安全的。
2.懶漢式
(懶漢式的實現方式有多種,最終演化出了一種最優的方式,此處只介紹這種最優方式)
public class DubbleSingleton { private static volatile DubbleSingleton ds; public static DubbleSingleton getDs(){ if (ds==null){ System.out.println(Thread.currentThread().getName()+":"+"第一輪判斷,我的ds==null,我要去搶建立物件的鎖啦!"); try { //模擬初始化物件的準備時間... System.out.println(Thread.currentThread().getName()+":"+"模擬初始化物件的準備時間..."); Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } synchronized (DubbleSingleton.class){ System.out.println(Thread.currentThread().getName()+":"+"第二輪判斷,我的ds==null,我來建立物件啦!"); if(ds==null){ System.out.println(Thread.currentThread().getName()+":"+"哈哈哈哈哈!是我建立的物件!"); ds = new DubbleSingleton(); }else { System.out.println(Thread.currentThread().getName()+":"+"第二輪判斷之後,我的ds!=null,看來有人在我之前把物件建立了!"); } } } return ds; } public static void main(String[] args){ Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode()); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode()); } },"t2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode()); } },"t3"); t1.start(); t2.start(); t3.start(); } }
請讀者先思考一個問題再繼續往下看:
為什麼要判斷兩次ds==null?
-------------------------------------------------------------思考分割線----------------------------------------------------------------
我們來看一下列印結果:
因為懶漢式是方法呼叫時再建立物件,所以在多執行緒高併發時為了保證安全,並且高效,在程式碼塊上加了synchronized關鍵字,多個執行緒可以同時呼叫getDS方法,這時他們的ds都是null,但當一個執行緒在建立一個物件後,由於其他執行緒已經在第一輪判斷時就進入了方法,所以其他執行緒會繼續走synchronized程式碼塊,這時進行第二輪判斷,其他執行緒會在第二輪判斷時發現物件已經建立,則不會再繼續建立物件。
就醬,OVER!