1 懶漢模式
此種最簡(jiǎn)單、方便,缺點(diǎn)可以忽略,<font color = 'red'>建議使用</font>
package com.xin.demo.sigle;
/**
* 懶漢模式,簡(jiǎn)單實(shí)用,推薦使用這種寫(xiě)法
* 類(lèi)加載到內(nèi)存后就實(shí)例化一個(gè)對(duì)象,jvm保證線程的安全
* 缺點(diǎn):不管是否使用,類(lèi)加載時(shí)就進(jìn)行實(shí)例化操作
*/
public class Single01 {
/**
* 類(lèi)加載時(shí)進(jìn)行對(duì)象的創(chuàng)建,jvm 保證類(lèi)線程安全
*/
private static final Single01 INSTANCE = new Single01();
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single01() {
}
public static Single01 getINSTANCE() {
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single01.getINSTANCE().hashCode());
}).start();
}
}
}
2 同1 靜態(tài)代碼塊的寫(xiě)法
package com.xin.demo.sigle;
/**
* 同01
*/
public class Single02 {
/**
* 類(lèi)加載時(shí)進(jìn)行對(duì)象的創(chuàng)建,jvm 保證類(lèi)線程安全
*/
private static final Single02 INSTANCE;
// 同01 只是改成了 靜態(tài)代碼塊
static {
INSTANCE = new Single02();
}
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single02() {
}
public static Single02 getINSTANCE() {
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single02.getINSTANCE().hashCode());
}).start();
}
}
}
3 餓漢模式(非線程安全)
按需分配,此方法存在線程安全問(wèn)題
package com.xin.demo.sigle;
/**
* 餓漢模式,按需分配
*/
public class Single03 {
private static Single03 INSTANCE = null;
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single03() {
}
public static Single03 getINSTANCE() {
if (INSTANCE == null) {
// 此處驗(yàn)證多線程并發(fā)的問(wèn)題,雖然此處達(dá)到了 按需分配,但是多線程時(shí)不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single03();
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single03.getINSTANCE().hashCode());
}).start();
}
}
}
4 餓漢模式(線程安全)
此處采用給方法上加 synchronized 關(guān)鍵字解決了線程安全問(wèn)題,同時(shí)帶來(lái)了性能降低的問(wèn)題,每次獲取實(shí)例時(shí)都會(huì)進(jìn)行等待。
package com.xin.demo.sigle;
/**
* 餓漢模式,按需分配
*/
public class Single04 {
private static Single04 INSTANCE = null;
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single04() {
}
public static synchronized Single04 getINSTANCE() {
// 加鎖 保證線程安全,但是這種方式帶來(lái)了 效率下降的問(wèn)題
if (INSTANCE == null) {
// 此處驗(yàn)證多線程并發(fā)的問(wèn)題,雖然此處達(dá)到了 按需分配,但是多線程時(shí)不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single04();
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single04.getINSTANCE().hashCode());
}).start();
}
}
}
5 餓漢模式(解決性能問(wèn)題)
此種存在問(wèn)題,通過(guò)代碼塊未達(dá)到目的,時(shí)6 的過(guò)渡。
package com.xin.demo.sigle;
/**
* 餓漢模式,按需分配
*/
public class Single05 {
private static Single05 INSTANCE = null;
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single05() {
}
public static Single05 getINSTANCE() {
if (INSTANCE == null) {
// 此處驗(yàn)證多線程并發(fā)的問(wèn)題,雖然此處達(dá)到了 按需分配,但是多線程時(shí)不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 為了提高效率 通過(guò)用代碼塊的方式進(jìn)行解決,如果為null 則加鎖,此方法 依然不可行,未解決多線程的問(wèn)題
synchronized (Single05.class) {
// 多線程時(shí) 第一處多個(gè)為null,有多個(gè)線程進(jìn)了此鎖,依然會(huì)產(chǎn)生多個(gè)對(duì)象
INSTANCE = new Single05();
}
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single05.getINSTANCE().hashCode());
}).start();
}
}
}
6 餓漢模式(完美的餓漢模式)
package com.xin.demo.sigle;
/**
* 餓漢模式,按需分配
*/
public class Single06 {
private static Single06 INSTANCE = null;
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single06() {
}
public static Single06 getINSTANCE() {
// 為了提高效率,如果為null 則不進(jìn)鎖
if (INSTANCE == null) {
// 此處驗(yàn)證多線程并發(fā)的問(wèn)題,雖然此處達(dá)到了 按需分配,但是多線程時(shí)不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 為了提高效率 通過(guò)用代碼塊的方式進(jìn)行解決,如果為null 則加鎖,此方法 依然不可行,未解決多線程的問(wèn)題
synchronized (Single06.class) {
// 多線程時(shí) 第一處多個(gè)為null,有多個(gè)線程進(jìn)了此鎖,依然會(huì)產(chǎn)生多個(gè)對(duì)象
// 因此 此處需要在進(jìn)行一次判斷,雙重判斷
if (INSTANCE == null) {
INSTANCE = new Single06();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single06.getINSTANCE().hashCode());
}).start();
}
}
}
7 通過(guò)內(nèi)部類(lèi)實(shí)現(xiàn)
package com.xin.demo.sigle;
/**
* 通過(guò)內(nèi)部類(lèi) 來(lái)實(shí)現(xiàn)加載外部類(lèi)時(shí)不會(huì)加載內(nèi)部類(lèi),這樣可以實(shí)現(xiàn)懶加載
*/
public class Single07 {
// 內(nèi)部類(lèi)不會(huì)在類(lèi)加載的時(shí)候加載,只有在調(diào)用的時(shí)候進(jìn)行加載,因此是按需分配,jvm保證了線程的安全
public static class SingleHolder {
protected static Single07 INSTANCE = new Single07();
}
/**
* 構(gòu)造器必須私有化,不允許外部進(jìn)行對(duì)象的創(chuàng)建
*/
private Single07() {
}
public static Single07 getINSTANCE() {
return SingleHolder.INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single07.getINSTANCE().hashCode());
}).start();
}
}
}
8 通過(guò)枚舉實(shí)現(xiàn)
effective java 中建議采用 枚舉來(lái)實(shí)習(xí)單例 ,<font color='red'>推薦使用</font>
package com.xin.demo.sigle;
/**
* 通過(guò) 枚舉保證了線程的安全,也可以防止反序列化
*/
public enum Single08 {
INSTANCE;
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single08.INSTANCE.hashCode());
}).start();
}
}
}
9 通過(guò)枚舉實(shí)現(xiàn)
枚舉可以實(shí)現(xiàn)接口,無(wú)法進(jìn)行繼承,大多數(shù)情況下還是需要用到類(lèi)的,因此我們通過(guò)在類(lèi)中嵌套枚舉以達(dá)到單列。
package com.xin.demo.sigle;
/**
* 通過(guò) 枚舉保證了線程的安全,也可以防止反序列化
*
* 枚舉沒(méi)法進(jìn)行 繼承,因此有時(shí)候通常需要類(lèi)來(lái)進(jìn)行操作,此處是將類(lèi) 通過(guò)枚舉來(lái)實(shí)現(xiàn)單列
*/
public class Single09 {
public static Single09 getINSTANCE() {
return Single09Enum.SINGLE09.getInstance();
}
public enum Single09Enum {
SINGLE09;
private final Single09 INSTANCE;
Single09Enum() {
INSTANCE = new Single09();
}
public Single09 getInstance() {
return INSTANCE;
}
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single09.getINSTANCE().hashCode());
}).start();
}
}
}