设计模式之单例模式

1.单例模式简介

单例模式,就是采取一定的方法保证在整个的软件系统中,对某一个类只存在一个对象实例,并且该类只提供一个取得其对象的方法。

 

2.单例模式的实现方法

单例模式的实现方法分为8种,主要分为:

    1.饿汉式(静态常量)(√)
    2.饿汉式(静态代码块)(√)
    3.懒汉式(线程不安全)
    4.懒汉式(线程安全,同步方法)
    5.懒汉式(线程安全,同步代码块)
    6.双重检查(√)
    7.静态内部类(√)
    8.枚举类(√)

 

2.1: 饿汉式(静态常量)

核心代码: private static SingletonByStatic  instance = new SingletonByStatic();

优点:简单,在类装载的时候已经完成了实例化,避免了线程同步

缺点:在类装载的时候就完成实例化,没有达到 lazyloading(懒加载),如果从始至终没有使用过这个实例,则会造成内存的浪费 。导致类装载的原因可能有很多种,这时候万一初始化了instance,未使用。造成了内存的浪费

完整代码:

package com.liz.GOF23.singleton.hungry_man;

//饿汉式(静态变量)

//优点: 简单,在类装载的时候已经完成了实例化,避免了线程同步

//缺点: 在类装载的时候就完成实例化,没有达到 lazyloading(懒加载),如果从始至终没有使用过这个实例,则会造成内存的浪费
//导致类装载的原因可能有很多种,这时候万一初始化了instance,未使用。造成了内存的浪费

//!!!可以使用,可能造成内存浪费
public class SingletonByStatic {
    //1.构造器私有化,外部不能new
    private SingletonByStatic(){

    }
    //2.本类内部创建对象实例(在类加载的时候进行定义 由于后期的构造器私有化 所以不能修改)
    private static  SingletonByStatic instance = new SingletonByStatic();
    //3.对外提供返回 返回实例对象
    public static SingletonByStatic getInstance(){
        return instance;
    }
    public static void main(String[] args) {
        //测试
        SingletonByStatic instance = SingletonByStatic.getInstance();
        SingletonByStatic instance1 = SingletonByStatic.getInstance();
        System.out.println(instance == instance1); //true
        System.out.println(instance.hashCode()); //完全一致
        System.out.println(instance1.hashCode());//完全一致
    }
}
 

 

2.2: 饿汉式 (静态代码块)

核心代码:

static{
    instance=new SingletonByStaticBlock();
}

优点:类加载的时候完成初始化,以静态代码块的形式完成了类的实例化,在类装载的时候,就执行静态代码块中的代码,初始化类的实例。

缺点:可能造成内存浪费

完整代码:

package com.liz.GOF23.singleton.hungry_man;
/**
 * 饿汉式(静态代码块)
 * 类加载的时候完成初始化
 *
 * 这种方法和上面的方法类似,只不过将类实例化的过程放在了静态代码块中。也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上述一致
 * 这种单例模式可用,但是可能造成内存浪费
 * */
public class SingletonByStaticBlock {
    private static SingletonByStaticBlock instance;//静态代码

    //在静态代码块中创建单例对象
    static{
        instance=new SingletonByStaticBlock();
    }
    private SingletonByStaticBlock(){}//防止对类进行修改

    public static SingletonByStaticBlock getInstance() {
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(SingletonByStaticBlock.getInstance().hashCode() == SingletonByStaticBlock.getInstance().hashCode());
    }
}

 

2.3 懒汉式(线程不安全)

核心代码:

if(instance == null){
    //延迟加载
    instance=new SingleBySync();
}

优点:实现了懒加载,提高了效率

缺点:只能在单线程下使用

完整代码:

public class SingleByLazyLoading{
    private static SingleByLazyLoading instance;

    private SingleByLazyLoading(){}

    public static SingleByLazyLoading getInstance(){
    //实现懒加载
        if(instance == null){
            //延迟加载
            instance=new SingleByLazyLoading();
        }
        return instance;

    }

    
    public static void main(String[] args) {
        System.out.println(SingleByLazyLoading.getInstance() ==(SingleByLazyLoading.getInstance()));
    }
}

 

 

2.4 懒汉式(同步方法)

核心代码:public static synchronized SingleBySync getInstance(){}

优点:解决了线程不安全的问题

缺点:效率太低。当每个线程想获得类实例的时候,执行getInstance方法都要进行同步,而这个方法同步一次就够了,后面想获得该实例直接返回即可。所以说效率过低(实际开发中不建议使用)

完整代码:

package com.liz.GOF23.singleton.lazy_man;

/**
 * 加重量级锁 保证线程安全
 * 懒汉式(线程安全,同步方法) 在使用的时候才进行创建
 *
 * 优点: 解决了线程不安全的问题
 * 缺点:效率太低。当每个线程想获得类实例的时候,执行getInstance方法都要进行同步,而这个方法同步一次就够了,后面想获得该实例直接返回即可。所以说效率过低
 *
 * !!!在实际开发中,不推荐使用(效率过低)
 * */
public class SingleBySync {
    private static SingleBySync instance;

    private SingleBySync(){}

    public static synchronized SingleBySync getInstance(){
        if(instance == null){
            //延迟加载
            instance=new SingleBySync();
        }
        return instance;

    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance 同时加上了synchronized 保证线程同步
    public static void main(String[] args) {
        System.out.println(SingleBySync.getInstance() == (SingleBySync.getInstance()));
    }
}

 

2.5 懒汉式  (同步代码块)

核心代码:

synchronized(SingletonByNotSafeThread.class){
    instance = new SingletonByNotSafeThread();
}

优点: 起到了懒加载的效果,但是只能在单线程下使用

缺点: 多线程下无效(一个线程进入了if(singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时候就会出现了多个实例。所以在多线程下不可取),同时效率还低.

完整代码:

package com.liz.GOF23.singleton.lazy_man;

/**
 * 懒汉式(线程不安全)【当使用某个方法时,才去创建instance】
 * 在实例化方法处写逻辑
 *
 * 优缺点声明:
 * 优点: 起到了懒加载的效果,但是只能在单线程下使用
 * 缺点: 如果在多线程下,一个线程进入了if(singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时候就会出现了多个实例。所以在多线程下不可取
 *
 * !!! 实际开发中,不可取
 * */
public class SingletonByNotSafeThread {
    private static SingletonByNotSafeThread instance;

    private SingletonByNotSafeThread(){}

    //不安全 效率还低(白给自爆性) 不能使用
    public static SingletonByNotSafeThread getInstance(){
        if(instance == null){
            synchronized(SingletonByNotSafeThread.class){
                instance = new SingletonByNotSafeThread();
            }
        }
        return instance;// 依旧不能保证线程安全
    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    public static void main(String[] args) {
        System.out.println(SingletonByNotSafeThread.getInstance() == (SingletonByNotSafeThread.getInstance()));
    }
}

 

2.6 双重检查

核心代码:

 //volatile: 立即更新到主存,同时保证了指令不重排序
private static volatile SingleByDoubleCheck singleton;
//双重检查代码
if(singleton == null){
    synchronized (SingleByDoubleCheck.class){
        if(singleton == null){
            singleton=new SingleByDoubleCheck();
        }
    }
}

优点: 线程安全,同时使用了延迟加载,效率高

缺点:代码略微复杂

全部代码:

package com.liz.GOF23.singleton.double_check;
/**
 * 双重选择:
 * 进行了两次if(singleton == null)的检查,保证了线程安全
 * 这样实例化代码只用执行一次,后面再次访问时,直接return即可
 *
 * 线程安全: 延迟加载,效率较高
 * 结论: 在实际开发中,用的较多
 * */
public class SingleByDoubleCheck {
    //volatile: 立即更新到主存,同时保证了指令不重排序
    private static volatile SingleByDoubleCheck singleton;
    private SingleByDoubleCheck(){}

    //提供一个静态的公有方法,加入双重检查代码。解决线程安全问题和懒加载问题
    public static SingleByDoubleCheck getInstance(){
        if(singleton == null){
            synchronized (SingleByDoubleCheck.class){
                if(singleton == null){
                    singleton=new SingleByDoubleCheck();
                }
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
        System.out.println(SingleByDoubleCheck.getInstance() == SingleByDoubleCheck.getInstance());
    }
}

 

2.7 静态内部类

核心代码

//静态内部类 
 private static class InnerClass {
        private static final StaticInnerClassDemo INSTANCE = new StaticInnerClassDemo();
    }

优点: 

1.避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。

2.达到了懒加载的效果{外部类的加载不会导致内部类的加载}

3.类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性。在类进行初始化时,其他线程无法进入

4.推荐使用

 

核心代码:

public class StaticInnerClassDemo {
    private StaticInnerClassDemo() {
    }


    private static class InnerClass {
        private static final StaticInnerClassDemo INSTANCE = new StaticInnerClassDemo();
    }

    /**
     * 静态内部类
     * 1.当主类进行类装载的时候,静态内部类不会进行装载
     * 2.当调用方法,用到了静态内部类时,内部类会进行装载
     */
    public static StaticInnerClassDemo getInstance(){
        return InnerClass.INSTANCE;
    }

    public static void main(String[] args) {
        System.out.println(StaticInnerClassDemo.getInstance() == StaticInnerClassDemo.getInstance());
    }
}

2.8: 枚举类

核心代码: 建立一个枚举类型

package com.liz.GOF23.singleton.enum_singleton;
//枚举
public enum Singleton {
    INSTANCE;//
    private  SingletonByEnumDemo demo = null;
    private Singleton() {
        demo = new SingletonByEnumDemo();
    }

    public SingletonByEnumDemo getInstance() {
        return demo;
    }
}

优点:

借助jdk1.5中添加的枚举类实现单例模式,不但可以避免多线程同步问题,而且防止反序列化重新创建新的对象

同时也采用了懒加载的思想

 

完整代码:

package com.liz.GOF23.singleton.enum_singleton;
//枚举
public enum Singleton {
    INSTANCE;//
    private  SingletonByEnumDemo demo = null;
    private Singleton() {
        demo = new SingletonByEnumDemo();
    }

    public SingletonByEnumDemo getInstance() {
        return demo;
    }
}


package com.liz.GOF23.singleton.enum_singleton;

/**
 * 借助jdk1.5中添加的枚举类实现单例模式,不但可以避免多线程同步问题,而且防止反序列化重新创建新的对象
 * */
public class SingletonByEnumDemo {
    public static void main(String[] args) {
        SingletonByEnumDemo instance1 = Singleton.INSTANCE.getInstance();
        SingletonByEnumDemo instance2 = Singleton.INSTANCE.getInstance();
        System.out.println(instance1 == instance2);
    }
}
 

 

3.单例模式总结

1.单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能

2.当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new (getInstance()..)

 

3.单例模式使用场景:

<1>.需要频繁的进行创建和销毁的对象

<2>.创建对象时耗时过多或者耗费资源过多(即:重量级对象),但是又经常用到的对象

<3>.工具类对象

<4>.频繁访问数据库或者文件的对象(sessionFactory or 数据源)

 

4.jdk中使用到单例模式的栗子: java.lang.Runtime(使用了上述的2.1 采用了静态常量的饿汉式实现)

 

 

 

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值