设计模式-单例模式

1.简单的介绍一下单例模式
(1)什么是单例模式?单例模式java里面常用的一种设计模式,他从系统启动,到结束,该类都只有一个实例对象在内存里面,并且提供了获取这个实例的方法。
(2)单例模式有哪几种实现的方式?饿汉模式,饿汉-静态代码块模式,懒汉-同步模式,懒汉-双重锁检查模式,懒汉-内部类延迟模式,注册模式
2.接着介绍饿汉模式:
(1)饿汉模式是在代码初始化的时候就创建对象的,懒汉模式是线程安全的
(2)具体的代码:

/**
 * ${DESC}
 *
 * @author michael
 * @date 2019-10-10 11:50
 */
public class HungerSingleton {
    //饿汉-静态方法的时候就创建
    private static HungerSingleton hungerSingleton = new HungerSingleton();

    public HungerSingleton() {

    }

    //获取实例
    public HungerSingleton getInstecs() {
        return hungerSingleton;
    }
}

(3)测试使用多线程去获取这个实例

public class HungerThread extends Thread {

    @Override
    public void run() {
        System.out.println(HungerSingleton.getInstecs());
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            HungerThread hungerThread = new HungerThread();
            hungerThread.start();
        }
    }
}

输出结果如下:

io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2
io.communet.ics.service.service.HungerSingleton@1f264dd2

可以确定饿汉模式的单例模式是线程安全的。
3.介绍饿汉-代码块模式:
对比与饿汉模式,他的区别只是在代码块里面创建实例对象而已,他的代码如下:

/**
 * ${DESC}
 *
 * @author michael
 * @date 2019-10-10 11:50
 */
public class HungerSingleton {
    //饿汉-静态方法的时候就创建
    private static HungerSingleton hungerSingleton = null;
    
    static {
        hungerSingleton=new HungerSingleton();
    }

    public HungerSingleton() {

    }

    //获取实例
    public static HungerSingleton getInstecs() {
        return hungerSingleton;
    }
}

同样的,他也是线程安全的。
4.接着我们介绍懒汉-同步模式
简单的懒汉模式他是线程不安全的,因此我们需要在懒汉模式的基础上做同步操作,具体的代码如下:

/**
 * ${DESC}
 *
 * @author michael
 * @date 2019-10-10 13:52
 */
public class LazySynSingleton {

    private static LazySynSingleton lazySynSingleton = null;

    public LazySynSingleton() {

    }

    public static synchronized LazySynSingleton getLazySynSingleton() {
        if (null == lazySynSingleton) {
            lazySynSingleton = new LazySynSingleton();
            return lazySynSingleton;
        } else {
            return lazySynSingleton;
        }
    }
}

运行,测试他的线程安全:

io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed
io.communet.ics.service.service.LazySynSingleton@219be5ed

5.接着我们就介绍懒汉-双重锁检查模式
关键点:双重检查:两次判断的是实例是否为空,第一次检查为空之后,要上全局锁(类锁)
代码如下:

/**
 * ${DESC}
 *
 * @author michael
 * @date 2019-10-10 13:52
 */
public class LazyDoubleCheckSingleton {

    private static LazyDoubleCheckSingleton lazySynSingleton = null;

    public LazyDoubleCheckSingleton() {

    }

    public static LazyDoubleCheckSingleton getLazyDoubleCheckSingleton() {
        //检查是否为空
        if (null == lazySynSingleton) {
            //如果为空这锁住这个类
            synchronized (LazyDoubleCheckSingleton.class) {
                //再次判断是否为空
                if (null == lazySynSingleton) {
                    lazySynSingleton = new LazyDoubleCheckSingleton();
                    return lazySynSingleton;
                } else {
                    return lazySynSingleton;
                }
            }
        } else {
            return lazySynSingleton;
        }
    }
}

运行测试线程安全:

io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0
io.communet.ics.service.service.LazyDoubleCheckSingleton@1a31a8d0

运行测试结果为线程安全了
6.关注一下单例对象的特殊情况:存在单例实例在序列化和反序列化的过程中,单例对象不唯一了,简单的代码验证如下:

public static void main(String[] args) throws Exception {
        //获取一个单例对象
        HungerSingleton hungerSingleton = HungerSingleton.getInstecs();
        //打印一下当前实例的内存地址
        System.out.println(hungerSingleton);
        //获取对象输入流
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/michael/test.obj"));
        //接着就是写入对象流
        objectOutputStream.writeObject(hungerSingleton);
        //关闭
        objectOutputStream.flush();
        objectOutputStream.close();

        //接着反序列化,由流生成对象
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/michael/test.obj"));
        Object object = objectInputStream.readObject();
        //输出一下内存地址
        System.out.println(object);
        //关闭流
        objectInputStream.close();
    }

运行结果如下:

io.communet.ics.service.service.HungerSingleton@51521cc1
io.communet.ics.service.service.HungerSingleton@368239c8

你会发现两个对象的内存地址反序列化之后,他单例对象的内存地址不一样了,说明不唯一了。
那么,我们要怎样解决单例实例序列化和反序列化之后单例不唯一的问题?
办法就是重写readResolve方法:
代码如下:

public class HungerSingleton implements Serializable {
    //饿汉-静态方法的时候就创建
    private static HungerSingleton hungerSingleton = null;

    static {
        hungerSingleton = new HungerSingleton();
    }

    public HungerSingleton() {

    }

    //获取实例
    public static HungerSingleton getInstecs() {
        return hungerSingleton;
    }


    public Object readResolve() {
        return hungerSingleton;
    }
}

运行结果如下:

io.communet.ics.service.service.HungerSingleton@51521cc1
io.communet.ics.service.service.HungerSingleton@51521cc1

可以发现重写了readResolve()方法后,序列化,反序列化内存地址布标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值