單例模式屬于java設(shè)計(jì)模式的一種倦畅,最常見實(shí)現(xiàn)方式有以下幾種 懶漢、餓漢礁凡、雙重檢查單例庐冯、靜態(tài)內(nèi)部類單例孽亲。
單例模式的特點(diǎn):
1:單例類只能有一個(gè)實(shí)例
2:單例類的唯一實(shí)例化必須由自己完成
3:單例類給其他對象提供唯一實(shí)例
如何保證第一個(gè)和第三個(gè)特點(diǎn)呢->2個(gè)實(shí)例化的對象相等說明是同一實(shí)例化對象
1 public class SingletonTest {
2? ?
3? ? public static void main(String[] args) {
4? ? ? ? Singleton singleton1=Singleton.getInstance();
5? ? ? ? Singleton singleton2=Singleton.getInstance();
6? ? ? ? /*
7? ? ? ? ? * 利用Set的特性檢驗(yàn)2個(gè)對象是同一個(gè)實(shí)例
8? ? ? ? ? * 輸出1代表這兩個(gè)變量代表的同一個(gè)實(shí)例對象
9? ? ? ? ? *
10? ? ? ? ? */
11? ? ? ? Set<Singleton> set=new HashSet<Singleton>();
12? ? ? ? set.add(singleton1);
13? ? ? ? set.add(singleton2);
14? ? ? ? System.out.println("set長度"+set.size());
15? ? ? ? //set長度1
16? ? }
17 }
如何理解第二個(gè)特點(diǎn):單例類是的實(shí)例化必須由自己完成->私有化構(gòu)造器
private Singleton() {
? }
1 package com.innerclass; 2? 3 public class SingletonTest { 4? ? ? 5? ? public static void main(String[] args) { 6? ? ? ? //我們在同包中創(chuàng)建一個(gè)其他類 并嘗試創(chuàng)建Singleton實(shí)例 得的一個(gè)錯(cuò)誤 7? ? ? ? //The constructor Singleton() is not visible 8? ? ? ? //構(gòu)造方法Singleton() 是不可見的 也就是說我們無法創(chuàng)建Singleton的實(shí)例對象 9? ? ? ? Singleton singleton=new Singleton();10? ? ? ? 11? ? }12 }
餓漢式的實(shí)現(xiàn)(餓漢式也就是不管你用不用我都把實(shí)例化創(chuàng)建好放在這里,你需要用的時(shí)候就拿去用)
優(yōu)點(diǎn):始終只有一個(gè)singleton實(shí)例對象 所以線程安全
? ? ? ? ? 在類加載的同時(shí)已經(jīng)創(chuàng)建好一個(gè)靜態(tài)對象展父,調(diào)用時(shí)反應(yīng)速度快
缺點(diǎn):jvm加載類的時(shí)候一定會實(shí)例化返劲,如果一直沒調(diào)用getInstance()方法玲昧,會造成資源的浪費(fèi)。
1 public class Singleton {2? private Singleton() {3? }4? private static Singleton singleton=new Singleton();5? public static Singleton getInstance() {6? ? ? return singleton;7? }8 }
線程安全的懶漢式(何為懶漢也就是按需加載 只有在使用的時(shí)候才對單例類去初始化)
優(yōu)點(diǎn):按需加載篮绿,不會造成資源的浪費(fèi)
缺點(diǎn):無synchronized關(guān)鍵字的單例類會造成線程的不同步
1? private Singleton() { 2? ? ? ? 3? } 4? public static Singleton singleton=null;? 5? public synchronized Singleton getInstance(){ 6? ? ? if(singleton==null) { 7? ? ? ? ? return? new Singleton(); 8? ? ? } 9? ? return singleton;10? }
此處說一下為什么要給getInstance()方法加鎖(實(shí)際意義上是給Singleton.class類類型加鎖孵延,有興趣可以去了解一下)
假設(shè)上面的代碼中沒有?synchronized?關(guān)鍵字
public class Singleton {? private Singleton() {? ? ? ? }? private static Singleton singleton=null;? public static? Singleton getInstance(){? ? ? if(singleton==null) {? ? ? ? ? try { //假設(shè)線程阻塞情況? ? ? ? ? ? Thread.sleep(100);? ? ? ? ? ? return? new Singleton();? ? ? ? } catch (InterruptedException e) {? ? ? ? ? ? // TODO Auto-generated catch block? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? ? ? ? ? ? }? return singleton;? }? public static void main(String[] args) {? ? ? ? Set singletons=? ? ? ? ? ? ? ? new HashSet();? ? ? ? for (int i = 0; i < 10; i++) {? ? ? ? ? ? singletons.add(Singleton.getInstance());? ? ? ? }? ? ? ? System.out.println(singletons.size()); //10 //說明多線程下懶漢式可能會創(chuàng)建多個(gè)實(shí)例對象}}
這種情況下,線程安全可以保證亲配,但是效率問題受到人的詬病了尘应。因?yàn)榫€程第一次實(shí)例化類之后,往后每次獲取實(shí)例化對象仍然需要去獲取單例類的鎖和釋放鎖吼虎。增加了性能的損耗菩收。于是有了以下2中進(jìn)階方式的單例模式
雙重檢查單例(不同于上一個(gè)懶漢式實(shí)現(xiàn)方式? 只有當(dāng)對象未實(shí)例化的時(shí)候才選擇去加鎖創(chuàng)建唯一實(shí)例,若是對象已初始化直接返回已初始化對象鲸睛,提高了效率)
1 public class Singleton { 2 /** 3? ? ? * 雙重檢查單例 4? ? ? */ 5? private Singleton() { 6? ? ? ? ? ? 7? } 8? private static? volatile? Singleton singleton; 9? public static Singleton getInstance() {10? ? ? if(singleton!=null) {11? ? ? ? ? synchronized (Singleton.class) {12? ? ? ? ? ? if(singleton!=null) {13? ? ? ? ? ? ? ? singleton=new Singleton();14? ? ? ? ? ? }15? ? ? ? }16? ? ? }17? ? ? return singleton;18? }19 }
volatile關(guān)鍵字 在這里不做敘述,有興趣的可以直接去百度它的作用
靜態(tài)內(nèi)部類實(shí)現(xiàn)單例(利用原理是內(nèi)部類的對外不可見性)
public class Singleton {? ? private Singleton() {? ? ? ? ? ? }? ? private static? class SingletonHandler{? ? ? ? private static Singleton singleton=new Singleton();? ? }? ? public Singleton getInstance() {? ? ? ? return SingletonHandler.singleton;? ? }? ? }
推薦大家在多線程開發(fā)中使用雙重檢查單例和靜態(tài)內(nèi)部類單例坡贺,集成了懶漢和餓漢的優(yōu)點(diǎn)官辈。
如何只是單線程沒有線程同步情況的話按照情況選擇懶漢和餓漢式。
學(xué)習(xí)過程中遍坟,如有不對拳亿,請指出。