Java设计模式详解 ------ 单例设计模式 懒汉+饿汉

设计模式专题—单例模式
今天机构开始带着复习设计模式 正巧我上周被临时的事情打断的自己复习代理模式也可以再听听看 话不多说 进入正题 ?

开始前照例先梳理概念:
什么是单例?

单例的好处是什么?

单例就是指: 同一个引用再堆内存中只指向同一个对象.(引用和堆不知道的,由于内容有点多 而且又太过基础 这里就不展开了 麻烦另行百度,顺带提一嘴 本文中如果遇上生僻词汇 那真的都是太基础的东西,烦请自行百度)

单例的好处: 因为Java是没有办法人工关闭由new关键字创建出的对象的,所有的被创建的对象都是由垃圾回收机制(GC)回收的,而垃圾回收机制又不受人为操控,所以 如果一味的创建对象,而没有及时回收,会导致一定性能问题.为解决这个问题,就有了单例设计模式

那明确了单例的由来 以及好处 那么 怎样实现单例呢?

  • 单例设计模式有两种
    • 懒汉模式
    • 饿汉模式
      • 提个题外话 是饿汉更懒还是懒汉更懒?
        应该是饿汉 因为 懒汉好歹不会饿着
    • 想了半天怎么说 都觉得没有直接上代码合适,那么 ?

饿汉单例模式

package com.liu.blogtesting.designpatterns.singlecase;

public class HungryMan {
	//饿汉单例模式 饿汉是天生线程安全的 但是有个问题:
	//因为是饿汉的实现是基于static的
	//就导致还未使用时就加载了,会浪费资源.
	//这里有解决方案:延迟加载

    //先把构造方法私有化 不让外部用new关键字创建
    private HungryMan(){}
    //然后新建需要单例类型的变量 赋值之后return出去
    private static final HungryMan hungryMan = new HungryMan();
		
	//由于是static 所以只会被创建一次 所以是单例的
    static HungryMan getHungryMan(){
        return hungryMan;
    }
}
  • 上述代码有一个小问题:还未使用时就加载了,会浪费资源,解决方案:延迟加载

饿汉升级版–>内部类解决提前加载浪费资源问题

package com.liu.blogtesting.designpatterns.singlecase;

public class HungryMan {
	//说下思路 把全局变量放到内部类中去,内部类没有被调用,
	//则全局变量也不会被调用,就不会存在预先加载问题
	
    //先把构造方法私有化 不让外部用new关键字创建
    private HungryMan(){}

    //这里创建一个内部类,内部类中实例化一个需要被单例的对象
    private static class TackHungryMan{
        static final HungryMan h = new HungryMan();
    }

    //return出去
    static HungryMan getHungryMan(){
        return TackHungryMan.h;
    }
}

懒汉单例模式

public class LazyMan {
    //懒汉单例是定义一个全局变量,在get方法中判断,如果为空才new 
    //如果不为空 则直接return 那这里虽然实现了单例 
    //但是会有一个问题:他是线程不安全的,
    //因为如果两个线程同时判断if 那么-->都不为空 就会创建两个对象

    private LazyMan(){}

    private static LazyMan lazyMan;

    static LazyMan getLazyMan(){
        if (lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

这里上一段测试代码和测试结果

public static void main(String[] args){
	//多线程下:
    new Thread(() -> {
        LazyMan lazyMan = LazyMan.getLazyMan();
        System.out.println(lazyMan);
    }).start();
    new Thread(() -> {
        LazyMan lazyMan = LazyMan.getLazyMan();
        System.out.println(lazyMan);
    }).start();
    // 未加双检索的结果:
    //com.liu.blogtesting.designpatterns.singlecase.LazyMan@6e622908
    //com.liu.blogtesting.designpatterns.singlecase.LazyMan@4e08a0b6
 }

这里解决问题:

//升级版懒汉
private LazyMan(){}
//这里加了一个volatile 避免极偶然的已经赋值却读到空的情况
private static volatile LazyMan lazyMan;
static LazyMan getLazyMan(){
    //这里先做判断,为了性能,如果不为null直接就return出去
    if (lazyMan == null){
        //如果为null 那么 加一层线程锁 防止同时判断
        synchronized (LazyMan.class){
            //这里再判断是否为null 那就是正儿八经的是否为null了
            if (lazyMan == null){
                //如果为null new一个对象
                lazyMan = new LazyMan();
            }
        }
    }
    return lazyMan;
}

测试代码:

//多线程下:
new Thread(() -> {
	LazyMan lazyMan = LazyMan.getLazyMan();
	System.out.println(lazyMan);
}).start();
new Thread(() -> {
	LazyMan lazyMan = LazyMan.getLazyMan();
	System.out.println(lazyMan);
}).start();
// 未加双检索的结果:
//com.liu.blogtesting.designpatterns.singlecase.LazyMan@6e622908
//com.liu.blogtesting.designpatterns.singlecase.LazyMan@4e08a0b6
//加了双检索的结果:
//com.liu.blogtesting.designpatterns.singlecase.LazyMan@793190d2
//com.liu.blogtesting.designpatterns.singlecase.LazyMan@793190d2

你可能感兴趣的:(设计模式专题)