每日一發(fā)設計模式 - 單例模式(singleton)

singleton

單例模式疾忍,是一種常用的軟件設計模式乔外。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統(tǒng)中一罩,應用該模式的類一個類只有一個實例杨幼。即一個類只有一個對象實例。

為什么要用單例模式

在實際的項目中,有一些東西是大家共用的差购,比如:系統(tǒng)配置四瘫、redis實例、數(shù)據(jù)庫實例等欲逃,我們不希望每個用戶在使用這些資源的時候找蜜,自己去新建一份實例,這樣會造成資源的浪費暖夭。
目的:保證一個類只有一個實例
使用場景:當你想控制實例個數(shù)锹杈,節(jié)省系統(tǒng)資源的時候
解決方案:判斷該類是否已經(jīng)有實例撵孤,如果有則返回迈着,沒有則創(chuàng)建
關鍵代碼:私有化構造函數(shù),讓使用方不能隨意的去實例
實現(xiàn)方案:懶漢邪码、餓漢裕菠、靜態(tài)內部類、枚舉

  1. 懶漢 - 線程不安全
package com.my.test.singleton;

//懶漢 - 線程不安全
public class SingletonExample01 {

    private SingletonExample01(){

    }

    private static SingletonExample01 instance = null;

    public static SingletonExample01 getInstance(){
        if(instance == null){
            instance = new SingletonExample01();
        }
        return instance;
    }
}
  1. 懶漢 - 線程安全
package com.my.test.singleton;

//懶漢 - 線程安全
public class SingletonExample02 {

    private SingletonExample02(){

    }

    private static SingletonExample02 instance = null;

    public static synchronized SingletonExample02 getInstance(){
        if(instance == null){
            instance = new SingletonExample02();
        }
        return instance;
    }
}
  1. 雙重校驗鎖
package com.my.test.singleton;

//懶漢 - 雙重校驗鎖
public class SingletonExample03 {

    private SingletonExample03(){

    }

    private static volatile SingletonExample03 instance = null;

    public static SingletonExample03 getInstance(){
        if(instance == null){
            synchronized (SingletonExample03.class){
                if(instance == null){
                    instance = new SingletonExample03();
                }
            }
        }
        return instance;
    }
}
  1. 餓漢模式
package com.my.test.singleton;

//餓漢模式
public class SingletonExample04 {

    private SingletonExample04(){

    }

    // 方法一
//    private static SingletonExample04 instance = new SingletonExample04();


    // 方法二
    private static SingletonExample04 instance = null;

    static {
        instance = new SingletonExample04();
    }

    public static SingletonExample04 getInstance(){
        return instance;
    }
}
  1. 靜態(tài)內部類
package com.my.test.singleton;

// 靜態(tài)內部類
public class SingletonExample05 {

    private SingletonExample05(){

    }

    private static class InstanceSingletonExample05{
        private static SingletonExample05 instance = new SingletonExample05();
    }

    public static SingletonExample05 getInstance(){
        return InstanceSingletonExample05.instance;
    }
}
  1. 枚舉
package com.my.test.singleton;


// 枚舉
public class SingletonExample06 {

    private SingletonExample06(){
    }

    public static SingletonExample06 getInstance(){
        return InstanceSingletonExample06.INSTANCE.getSingletonExample06();
    }

    private enum InstanceSingletonExample06{
        INSTANCE;

        private SingletonExample06 singletonExample06 = new SingletonExample06();

        public SingletonExample06 getSingletonExample06(){
            return singletonExample06;
        }
    }
}
  1. 枚舉2.0
package com.my.test.singleton;

public enum SingletonEnumExample{
    INSTANCE;

    public void test(){
        // do everything
    }
}

實例場景之 - 獲取jdbc連接

我們新增兩個類闭专,第一個是非單例的獲取jdbc連接

package com.mk.designDemo.singleton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MDataSourceNoSingle {

    private static final Logger logger = LoggerFactory.getLogger(MDataSourceNoSingle.class);

    private static Connection connection = null;

    public Connection getConnection() {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://******:3306/*****?characterEncoding=UTF-8", "root", "root");
            logger.info("線程{}實例化connection", Thread.currentThread().getName());
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

}

第二個使用了單例模式

package com.mk.designDemo.singleton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MDataSourceSingle {

    private static final Logger logger = LoggerFactory.getLogger(MDataSourceSingle.class);

    private static Connection connection = null;

    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://******:3306/*****?characterEncoding=UTF-8", "root", "root");
            logger.info("線程{}實例化connection", Thread.currentThread().getName());
        } catch (ClassNotFoundException | SQLException e) {
            logger.error(e.getMessage(), e);
        }
    }
    
    private MDataSourceSingle() {
    }

    public static Connection getConnection() {
        return connection;
    }
}

然后我們寫了一個簡單的controller奴潘,里面有兩個接口,用來驗證結果

package com.mk.designDemo.controller;

import com.mk.designDemo.singleton.MDataSourceNoSingle;
import com.mk.designDemo.singleton.MDataSourceSingle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Connection;

@RestController
public class SingletonController {

    private static final Logger logger = LoggerFactory.getLogger(SingletonController.class);

    private final static String sql = "select * from t_trade_date limit 10";

    @RequestMapping(path = "/noSingleton")
    public String noSingleton() {
        MDataSourceNoSingle mDataSourceNoSingle = new MDataSourceNoSingle();
        Connection connection = mDataSourceNoSingle.getConnection();
        doExecute(connection, sql);
        return "OK";
    }

    @RequestMapping(path = "/singleton")
    public String singleton() {
        doExecute(MDataSourceSingle.getConnection(), sql);
        return "OK";
    }


    private void doExecute(Connection connection, String sql) {
        logger.info("do execute sql:{}", sql);
    }

}

當我們調用三次非單例的接口時影钉,查看日志輸出如下画髓,connection被實例化了多次:

2019-07-09 15:47:31.966 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={}
2019-07-09 15:47:31.970 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton()
2019-07-09 15:47:32.633 [http-nio-80-exec-1] INFO  c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-1實例化connection
2019-07-09 15:47:32.633 [http-nio-80-exec-1] INFO  c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:47:32.671 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:47:32.672 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:47:32.693 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:47:40.005 [http-nio-80-exec-4] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={}
2019-07-09 15:47:40.007 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton()
2019-07-09 15:47:40.361 [http-nio-80-exec-4] INFO  c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-4實例化connection
2019-07-09 15:47:40.361 [http-nio-80-exec-4] INFO  c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:47:40.362 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:47:40.362 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:47:40.363 [http-nio-80-exec-4] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:47:48.459 [http-nio-80-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={}
2019-07-09 15:47:48.461 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton()
2019-07-09 15:47:48.801 [http-nio-80-exec-7] INFO  c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-7實例化connection
2019-07-09 15:47:48.801 [http-nio-80-exec-7] INFO  c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:47:48.802 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:47:48.802 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:47:48.803 [http-nio-80-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK

然后我們調用單例的接口三次,日志輸出如下平委,我們可以看到,只有第一次進行了實例化廉赔,后面再也沒有繼續(xù)實例化

2019-07-09 15:49:22.868 [http-nio-80-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={}
2019-07-09 15:49:22.871 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton()
2019-07-09 15:49:23.234 [http-nio-80-exec-10] INFO  c.m.d.singleton.MDataSourceSingle - 線程http-nio-80-exec-10實例化connection
2019-07-09 15:49:23.235 [http-nio-80-exec-10] INFO  c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:49:23.237 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:49:23.237 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:49:23.239 [http-nio-80-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:49:25.032 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={}
2019-07-09 15:49:25.034 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton()
2019-07-09 15:49:25.034 [http-nio-80-exec-1] INFO  c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:49:25.035 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:49:25.036 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:49:25.038 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK
2019-07-09 15:49:25.926 [http-nio-80-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={}
2019-07-09 15:49:25.926 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton()
2019-07-09 15:49:25.927 [http-nio-80-exec-2] INFO  c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10
2019-07-09 15:49:25.928 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-07-09 15:49:25.928 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"]
2019-07-09 15:49:25.929 [http-nio-80-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK

驗證通過碉纳,OVER铁孵!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烫止,隨后出現(xiàn)的幾起案子蒋荚,更是在濱河造成了極大的恐慌吼渡,老刑警劉巖得滤,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阿趁,死亡現(xiàn)場離奇詭異,居然都是意外死亡脖阵,警方通過查閱死者的電腦和手機皂股,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來命黔,“玉大人呜呐,你說我怎么就攤上這事『纺迹” “怎么了蘑辑?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坠宴。 經(jīng)常有香客問我洋魂,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任副砍,我火速辦了婚禮衔肢,結果婚禮上,老公的妹妹穿的比我還像新娘豁翎。我一直安慰自己角骤,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布心剥。 她就那樣靜靜地躺著邦尊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天啃炸,我揣著相機與錄音,去河邊找鬼疑苫。 笑死,一個胖子當著我的面吹牛纷责,可吹牛的內容都是我干的。 我是一名探鬼主播撼短,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼再膳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了曲横?” 一聲冷哼從身側響起喂柒,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎禾嫉,沒想到半個月后灾杰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡熙参,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年艳吠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孽椰。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡昭娩,死狀恐怖,靈堂內的尸體忽然破棺而出黍匾,到底是詐尸還是另有隱情栏渺,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布锐涯,位于F島的核電站磕诊,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜霎终,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一融痛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧神僵,春花似錦雁刷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炮障,卻和暖如春目派,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胁赢。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工企蹭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人智末。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓谅摄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親系馆。 傳聞我的和親對象是個殘疾皇子送漠,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內容