單例模式屬于java設(shè)計(jì)模式的一種,最常見(jiàn)實(shí)現(xiàn)方式有以下幾種 懶漢、餓漢、雙重檢查單例、靜態(tài)內(nèi)部類單例。
單例模式的特點(diǎn):
1:?jiǎn)卫愔荒苡幸粋€(gè)實(shí)例
2:?jiǎn)卫惖奈ㄒ粚?shí)例化必須由自己完成
3:?jiǎn)卫惤o其他對(duì)象提供唯一實(shí)例
如何保證第一個(gè)和第三個(gè)特點(diǎn)呢->2個(gè)實(shí)例化的對(duì)象相等說(shuō)明是同一實(shí)例化對(duì)象
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è)對(duì)象是同一個(gè)實(shí)例
8? ? ? ? ? * 輸出1代表這兩個(gè)變量代表的同一個(gè)實(shí)例對(duì)象
9? ? ? ? ? *
10? ? ? ? ? */
11? ? ? ? Set<Singleton> set=new HashSet<Singleton>();
12? ? ? ? set.add(singleton1);
13? ? ? ? set.add(singleton2);
14? ? ? ? System.out.println("set長(zhǎng)度"+set.size());
15? ? ? ? //set長(zhǎng)度1
16? ? }
17 }
如何理解第二個(gè)特點(diǎn):?jiǎn)卫愂堑膶?shí)例化必須由自己完成->私有化構(gòu)造器
private Singleton() {
? }
1 package com.innerclass; 2? 3 public class SingletonTest { 4? ? ? 5? ? public static void main(String[] args) { 6? ? ? ? //我們?cè)谕袆?chuàng)建一個(gè)其他類 并嘗試創(chuàng)建Singleton實(shí)例 得的一個(gè)錯(cuò)誤 7? ? ? ? //The constructor Singleton() is not visible 8? ? ? ? //構(gòu)造方法Singleton() 是不可見(jiàn)的 也就是說(shuō)我們無(wú)法創(chuàng)建Singleton的實(shí)例對(duì)象 9? ? ? ? Singleton singleton=new Singleton();10? ? ? ? 11? ? }12 }
餓漢式的實(shí)現(xiàn)(餓漢式也就是不管你用不用我都把實(shí)例化創(chuàng)建好放在這里,你需要用的時(shí)候就拿去用)
優(yōu)點(diǎn):始終只有一個(gè)singleton實(shí)例對(duì)象 所以線程安全
? ? ? ? ? 在類加載的同時(shí)已經(jīng)創(chuàng)建好一個(gè)靜態(tài)對(duì)象,調(diào)用時(shí)反應(yīng)速度快
缺點(diǎn):jvm加載類的時(shí)候一定會(huì)實(shí)例化,如果一直沒(méi)調(diào)用getInstance()方法,會(huì)造成資源的浪費(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í)候才對(duì)單例類去初始化)
優(yōu)點(diǎn):按需加載,不會(huì)造成資源的浪費(fèi)
缺點(diǎn):無(wú)synchronized關(guān)鍵字的單例類會(huì)造成線程的不同步
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? }
此處說(shuō)一下為什么要給getInstance()方法加鎖(實(shí)際意義上是給Singleton.class類類型加鎖,有興趣可以去了解一下)
假設(shè)上面的代碼中沒(méi)有?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 //說(shuō)明多線程下懶漢式可能會(huì)創(chuàng)建多個(gè)實(shí)例對(duì)象}}
這種情況下,線程安全可以保證,但是效率問(wèn)題受到人的詬病了。因?yàn)榫€程第一次實(shí)例化類之后,往后每次獲取實(shí)例化對(duì)象仍然需要去獲取單例類的鎖和釋放鎖。增加了性能的損耗。于是有了以下2中進(jìn)階方式的單例模式
雙重檢查單例(不同于上一個(gè)懶漢式實(shí)現(xiàn)方式? 只有當(dāng)對(duì)象未實(shí)例化的時(shí)候才選擇去加鎖創(chuàng)建唯一實(shí)例,若是對(duì)象已初始化直接返回已初始化對(duì)象,提高了效率)
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)部類的對(duì)外不可見(jiàn)性)
public class Singleton {? ? private Singleton() {? ? ? ? ? ? }? ? private static? class SingletonHandler{? ? ? ? private static Singleton singleton=new Singleton();? ? }? ? public Singleton getInstance() {? ? ? ? return SingletonHandler.singleton;? ? }? ? }
推薦大家在多線程開(kāi)發(fā)中使用雙重檢查單例和靜態(tài)內(nèi)部類單例,集成了懶漢和餓漢的優(yōu)點(diǎn)。
如何只是單線程沒(méi)有線程同步情況的話按照情況選擇懶漢和餓漢式。
學(xué)習(xí)過(guò)程中,如有不對(duì),請(qǐng)指出。