簡單了解Java單例模式(Singleton)

http://c.biancheng.net/view/1338.html
https://www.cnblogs.com/zhucj-java/p/13534551.html
https://www.cnblogs.com/restartyang/articles/7770856.html
https://www.imooc.com/article/310708

單例模式定義

單例模式是指一個(gè)類只有一個(gè)實(shí)例,而且該類可以自己創(chuàng)建自己的實(shí)例。

單例模式的應(yīng)用

1.在高并發(fā)的系統(tǒng)中生成單號要求唯一,可以將生成單號的實(shí)例寫為單例模式
2.數(shù)據(jù)庫中的連接池
3.枚舉類
4.系統(tǒng)中的上下文
5.spring中@Autowired默認(rèn)注入的是單例注入
6.spring中在需要@Autowired默認(rèn)注入的是多例注入時(shí)可在需要注入的類加上@Scope(value="prototype")---prototype原型模式,每次獲取Bean的時(shí)候會有一個(gè)新的實(shí)例

單例模式的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1.由于在系統(tǒng)內(nèi)存中只存在一個(gè)對象,因此可以 節(jié)約系統(tǒng)資源,當(dāng) 需要頻繁創(chuàng)建和銷毀的對象時(shí)單例模式無疑可以提高系統(tǒng)的性能。
2.單例模式設(shè)置全局訪問點(diǎn),可以優(yōu)化和共享資源的訪問。
3.避免對共享資源的多重占用。

缺點(diǎn)

1.單例模式?jīng)]有接口,擴(kuò)展困難,則除了修改原來的代碼,沒有第二種途徑,違背開閉原則。

單例模式的實(shí)現(xiàn)

分為餓漢式和懶漢式、其中餓漢式在類被加載時(shí)就創(chuàng)建了實(shí)例、懶漢式在第一次調(diào)用時(shí)創(chuàng)建實(shí)例(在多線程下有線程安全問題)

餓漢式實(shí)現(xiàn)

/**
 * 單例模式-餓漢式
 * 餓漢式不管有沒有調(diào)用getInstance()方法,都會預(yù)先在系統(tǒng)中創(chuàng)建一個(gè)靜態(tài)對象
 * 餓漢式優(yōu)點(diǎn): 在多線程模式下是安全的
 * 缺點(diǎn): 沒有調(diào)用方法前就被加載,會占用內(nèi)存
 */
public class BaJie extends JPanel {
    private static volatile BaJie instance = new BaJie();

    private BaJie(){
        JLabel j = new JLabel(new ImageIcon("src/main/resources/img/Bajie.jpg"));
        this.add(j);
    }
    public static BaJie getInstance(){
        return instance;
    }

    public static void main(String[] args) {
        JFrame jf = new JFrame("餓漢式單例模式測試");
        jf.setLayout(new GridLayout(1, 2));
        Container contentPane = jf.getContentPane();
        BaJie baJie = BaJie.getInstance();
        contentPane.add(baJie);
        BaJie baJie2 = BaJie.getInstance();
        contentPane.add(baJie2);

        if(baJie == baJie2){
            System.out.println("他們是同一個(gè)人");
        } else {
            System.out.println("他們不是同一個(gè)人");
        }
        jf.pack();
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

運(yùn)行結(jié)果:

他們是同一個(gè)人

懶漢式實(shí)現(xiàn)

為解決在多線程下不安全對創(chuàng)建實(shí)例的方法加鎖synchronized

/**
 * 單例模式--懶漢式
 * 該模式的特點(diǎn)是類加載時(shí)沒有生成單例,只有當(dāng)?shù)谝淮握{(diào)用 getlnstance 方法時(shí)才去創(chuàng)建這個(gè)單例
 * 懶漢式優(yōu)點(diǎn):只有調(diào)用方法才創(chuàng)建對象,不會占用內(nèi)存
 * 缺點(diǎn):在多線程模式下不安全
 */
public class President {

    private static volatile President instance = null;

    //private 避免類在外部被實(shí)例化
    private President(){
        System.out.println("生產(chǎn)一個(gè)美國總統(tǒng)");
    }

    /**
     * synchronized為解決多線程下懶漢式存在的線程安全問題
     * synchronized 給對象和方法或代碼塊加鎖,使得同一時(shí)刻只有一個(gè)線程執(zhí)行該代碼
     * @return
     */
    public static synchronized President getInstance(){
        if (instance == null){
            instance = new President();
        } else {
            System.out.println("已有美國總統(tǒng),不能再生產(chǎn)!");
        }
        return instance;
    }

    public void getName(){
        System.out.println("獲得一個(gè)美國總統(tǒng):特朗普");
    }

    public static void main(String[] args) {

        //線程1
        Thread thread1 = new Thread(){

            @Override
            public void run(){
                President d1 = President.getInstance();
                System.out.println(d1.hashCode());
            }
        };

        thread1.start();

        //線程2
        Thread thread2 = new Thread(){

            @Override
            public void run(){
                President d2 = President.getInstance();
                System.out.println(d2.hashCode());
            }
        };
        thread2.start();
        
        President d3 = President.getInstance();
        System.out.println(d3.hashCode());
    }
}

main執(zhí)行結(jié)果:

生產(chǎn)一個(gè)美國總統(tǒng)
338093198
已有美國總統(tǒng),不能再生產(chǎn)!
338093198
已有美國總統(tǒng),不能再生產(chǎn)!
338093198

在多線程下通過加鎖可以解決線程安全問題
如果不加synchronized執(zhí)行結(jié)果為:

生產(chǎn)一個(gè)美國總統(tǒng)
生產(chǎn)一個(gè)美國總統(tǒng)
1587888397
已有美國總統(tǒng),不能再生產(chǎn)!
1368198263
1587888397

產(chǎn)生了1587888397、1368198263兩個(gè)實(shí)例

擴(kuò)展為有限的多例模式Multitcm

生成指定個(gè)數(shù)的實(shí)例存到Map中,使用時(shí)隨機(jī)取一個(gè)使用

有限的多例模式實(shí)現(xiàn)

/**
 * 多例模式Multitcm
 * 維護(hù)一個(gè)指定數(shù)量的對象池,當(dāng)請求個(gè)數(shù)超過控制的總數(shù)時(shí),開始循環(huán)重復(fù)使用
 */
public class DuoLi {
    private static int num = 4; //對象數(shù)量
    private static int count = 1; //對象序號
    private static Map<Integer, DuoLi> map = new HashMap();
    static {
        for (int i=1; i<=num; i++){
            map.put(i, new DuoLi());
        }
    }

    private DuoLi(){

    }

    public static DuoLi getInstance(){
        DuoLi duoLi = map.get(count);
        System.out.println(String.format("我是第%s個(gè)實(shí)例", count));
        count++;
        if (count > num){
            count = 1;
        }
        return duoLi;
    }

    public static void main(String[] args) {
        for (int i=0; i<5; i++){
            DuoLi duoLi = DuoLi.getInstance();
            System.out.println(duoLi.hashCode());
        }
    }
}

執(zhí)行結(jié)果為

我是第1個(gè)實(shí)例
1558600329
我是第2個(gè)實(shí)例
636718812
我是第3個(gè)實(shí)例
445051633
我是第4個(gè)實(shí)例
1051754451
我是第1個(gè)實(shí)例
1558600329

第五次取的對象為第一個(gè)實(shí)例

剩下的問題

已知類的創(chuàng)建方式有 new,反射、序列化、克隆
在單例模式由于構(gòu)建方法被私有化不可通過new創(chuàng)建,那么反射、序列化、克隆是否可以創(chuàng)建第二個(gè)實(shí)例呢?
https://my.oschina.net/u/3568600/blog/1827375

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容