Java單例模式的知識點詳解
懶漢模式與餓漢模式
懶漢模式就是懶加載,用到的時候去加載,存在線程安全問題,需要手動地加鎖控制。它的優(yōu)點是類加載的速度比較快,按需加載,節(jié)省資源。
餓漢模式就是在類加載的時候會創(chuàng)建出實例。它天生就不存在線程安全問題。但是類加載的速度會變慢且耗費資源。
懶漢模式-單重檢查
示例代碼如下:
public class LazySingleton {
private static LazySingleton singletoninstance = null;
private Object data = new Object();
//私有化構(gòu)造方法
private LazySingleton(){
}
//加鎖訪問
public static synchronized LazySingleton getInstance(){
if(singletoninstance == null){
singletoninstance = new LazySingleton();
}
return singletoninstance;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
測試代碼如下:
public class TestThread extends Thread {
@Override
public void run() {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance.getData());
}
}
public static void main(String[] args) {
for(int i =0;i < 10;i++){
TestThread t = new TestThread();
t.start();
}
}
}
運行結(jié)果如下:
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
打印出同一個object對象,表明是從同一個LazySingleton對象中獲取的數(shù)據(jù)。
但是上述代碼存在一個顯著的問題:多個線程同時訪問getInstance()方法都是排隊式的,即使該instance已經(jīng)被創(chuàng)建的情況下。然而,如果該instance已經(jīng)被創(chuàng)建,是可以支持并發(fā)訪問的。需要對鎖的控制細粒度化。
懶漢模式-雙重檢查
public class LazySingleton {
//聲明為volatile變量
private static volatile LazySingleton singletoninstance = null;
private Object data = new Object();
private LazySingleton(){
}
public static synchronized LazySingleton getInstance(){
if(singletoninstance == null){
synchronized (LazySingleton.class) {
//這個第二重的的檢查是必要的
if(singletoninstance == null)
singletoninstance = new LazySingleton();
}
}
return singletoninstance;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
第二重檢查是為了防止:
線程A發(fā)現(xiàn)instance未被創(chuàng)建,于是申請鎖,進入臨界區(qū)創(chuàng)建instance;于此同時另一個線程也發(fā)現(xiàn)instance未被創(chuàng)建,于是也要申請鎖去創(chuàng)建instance,問題就這樣發(fā)生了。而且,這個instance變量要被聲明為volatile,也就是其中一個線程對它就行修改之后(也就是實例化),這一修改立馬對其他線程可見,避免了無謂的等待。
檢查代碼同上,運行結(jié)果同上。
餓漢模式
public class HungerSingleton {
private static final HungerSingleton singletoninstance = new HungerSingleton();
private Object data = new Object();
private HungerSingleton(){
}
public static HungerSingleton getInstance(){
return singletoninstance;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
在加載該類的時候就立馬去實例化instance,不存在線程安全問題(由jvm保證線程安全問題),但是存在資源浪費、加載速度慢的問題。
檢查代碼同上,運行結(jié)果同上。
Holder模式
就是利用一個靜態(tài)內(nèi)部類來實現(xiàn)instance的實例化。這里利用了靜態(tài)內(nèi)部類的一個特性:該內(nèi)部類的實例與外部類的實例 沒有綁定關(guān)系,而且只有被調(diào)用到才會裝載,從而實現(xiàn)了延遲加載
public class HolderSingleton {
private Object data = new Object();
private HolderSingleton(){
}
private static class InnerClass{
private static HolderSingleton singletoninstance = new HolderSingleton();
}
public static HolderSingleton getInstance(){
return InnerClass.singletoninstance;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
測試代碼同上,運行結(jié)果同上。
在加載InnerClass的時候才會去實例化這個instance,實現(xiàn)了延遲加載,并且同餓漢模式一樣,由jvm保證線程安全。這種方法值得推薦。
應(yīng)用場景:
在整個系統(tǒng)中,只允許共用一個實例的類適合用單例模式來實現(xiàn),比如:
網(wǎng)站的計數(shù)器,只允許存在一個計數(shù)器實例;
線程池,只允許存在一個線程池對象;
連接池,只允許存在一個連接池對象;
知識點擴充:
1.為什么要使用單例模式?
在我們?nèi)粘5墓ぷ髦校芏鄬ο笸ǔU加梅浅V匾南到y(tǒng)資源,比如:IO處理,數(shù)據(jù)庫操作等,那我們必須要限制這些對象只有且始終使用一個公用的實例,即單例。
2.單例模式的實現(xiàn)方式
構(gòu)造函數(shù)私有化,防止其他類生成唯一公用實例外的實例。且單例類應(yīng)該被定義為final,也就是說單例類不能被繼承,因為如果允許繼承那子類就都可以創(chuàng)建實例,違背了類唯一實例的初衷。
類中一個靜態(tài)變量來保存單實例的引用。
一個共有的靜態(tài)方法來獲取單實例的引用。
3.單例模式的UML類圖

4.單例模式的經(jīng)典實現(xiàn)方式
- 餓漢式:一開始就創(chuàng)建好實例,每次調(diào)用直接返回,經(jīng)典的“拿空間換時間”。
- 懶漢式:延遲加載,第一次調(diào)用的時候才加載,然后返回,以后的每次的調(diào)用就直接返回。經(jīng)典“拿時間換空間”,多線程環(huán)境下要注意解決線程安全的問題。
- 登記式:對一組單例模式進行的維護,主要是在數(shù)量上的擴展,通過線程安全的map把單例存進去,這樣在調(diào)用時,先判斷該單例是否已經(jīng)創(chuàng)建,是的話直接返回,不是的話創(chuàng)建一個登記到map中,再返回。
以上就是本次腳本之家小編結(jié)合多篇整理的相關(guān)內(nèi)容,希望能夠幫助到大家。
相關(guān)文章
springboot項目連接多種數(shù)據(jù)庫該如何操作詳析
在Spring Boot應(yīng)用中連接多個數(shù)據(jù)庫或數(shù)據(jù)源可以使用多種方式,下面這篇文章主要給大家介紹了關(guān)于springboot項目連接多種數(shù)據(jù)庫該如何操作的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-08-08
Spring Boot @Conditional注解用法示例介紹
這篇文章主要給大家介紹了關(guān)于Spring Boot @Conditional注解用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring Boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-11-11

