单例设计模式

一. 单例模式介绍

什么是单例模式及优点:

保证某个类在项目中只存在一个实例,并且该类只提供一个获取其实例的方法,节省系统资源,对需要频繁创建销毁的对象使用单例模式,可以提高系统性能

使用场景

需要频繁创建,销毁的对象,创建时耗费资源较大的,又频繁用到的,例如工具类对象,访问数据库或访问文件的,例如Hibemate的SessionFactory

JDK源码分析单例模式

例如JDK中,java.lang.Runtime使用的就是饿汉式单例模式
在这里插入图片描述

二. 单例模式的八种实现方法

1. 饿汉式

实现步骤

  1. 创建需要单例的类
  2. 私有化构造器,防止外部通过new创建实例
  3. 在类的内部创建一个静态的私有化实例(以属性存在)
  4. 创建一个静态的公共方法,向外暴露类中创建的这个实例

分析优缺点

  1. 优点: 写法简单,由于在类的内部创建的实例是静态的,所以在该类装载时就完成了实例化,通过classloder机制避免线程同步问题
  2. 缺点: 在类装载时完成实例化,会出现一个问题,如果此次的系统运行并没有用到这个实例,则会造成内存浪费,
  3. 分析得出: 在项目中如果要进行实例化,并且该实例在程序运行时一定会用到,可以推荐此方式

代码示例

//饿汉式一: 静态常量
class Singleton{
    //1.私有化构造器,不允许通过new创建其他对象
    private Singleton(){

    }
    //2.提供一个私有的静态的实例对象
    private final static Singleton instance = new Singleton();

    //3.通过公关的静态方法将静态实例对外暴露
    //(由于上方创建的实例是静态的,存在静态域中,
    //不管外部调用几次该方法,实际上获取到的是同一个实例)
    public static Singleton getInstance(){
        return instance;
    }
}
//饿汉式二: 静态代码块
class Singleton2{
    //1.声明一个私有的静态的实例对象,注意此处只是声明
    private static Singleton2 instance;

    //2.私有化构造器,不允许通过new创建其他对象
    private Singleton2(){
    }

    //3.创建静态代码块,在代码块中对声明的实例进行赋值
    static{
        instance = new Singleton2();
    }

    //4.通过公关的静态方法将静态实例对外暴露
    //(由于上方创建的实例是静态的,存在静态域中,
    //不管外部调用几次该方法,实际上获取到的是同一个实例)
    public static Singleton2 getInstance(){
        return instance;
    }
}

2. 懒汉式一(双重检查式解决线程安全问题)

实现步骤

  1. 创建需要单例的类
  2. 在类的内部声明该类的实例(注意只是声明)
  3. 私有化构造器,防止外部通过new创建实例
  4. 提供一个公共的静态方法,在方法内判断声明的实例是否为空,当为空时才去创建,将实例暴露
  5. 注意线程安全问题及效率问题,使用volatile修饰声明的实例,在合适的位置使用synchronized修饰创建实例的代码(代码示例中有分析)

分析优缺点

  1. 懒汉式加载在需要的时候才去进行实例化,需要考虑线程安全问题,使用synchronized修饰,考虑效率问题,注意synchronized修饰的位置,使用volatile修饰,设置线程可见性,防止引用溢出等

代码示例

//懒汉式: 线程安全问题及synchronized解决线程安全问题
class Singleton3{

    //1.声明一个私有的静态的实例对象,注意此处只是声明
    private static Singleton3 instance;

    //2.私有化构造器,不允许通过new创建其他对象
    private Singleton3(){

    }

    //3.提供公共的静态方法,对外暴露实例,注意点:
    //上方的实例只是声明,在方法内部对什么进行实例化
    //当有调用到该方法时才进行实例化
    public static Singleton3 getInstance(){
        //如果声明的instance等于null时才去new
        //否则说明已经创建过了直接返回即可
        if( null == instance){
            //业务代码.....
            return new Singleton3();
        }
        return  instance;
    }

    //分析第3步骤可能存在的问题:
    //在多线程的情况下,假设当线程A走到业务代码,进行业务处理或阻塞,
    //还没有创建对象时,线程B调用该方法,判断instance实例也是空,
    //会再次创建一个实例对象并返回,此时就出现了多个实例对象

    //4.解决线程安全问题一:
    //使用 synchronized 修饰创建实例并返回的方法,在同一时间内保证只有一个线程执行该方法
    public static synchronized Singleton3 getInstance2(){
        if( null == instance){
            //业务代码.....
            return new Singleton3();
        }
        return  instance;
    }

    //分析第4步,虽然解决了线程安全问题,但是会出现一个问题: 假设该方法在项目中会多次用到
    //每当调用该方法时,都需要判断是否有线程正在执行,如果有阻塞等待,效率太低,使用双重检查

}
//懒汉式: 双重检查,防止线程安全问题,及效率低的问题
class Singleton4{

    //1.声明一个私有的静态的实例对象,注意此处只是声明
    //并且使用volatile进行修饰,设置当前线程对该变量
    //进行修改时,其它线程的及时可见性,并防止指令重排,防止反序列化时可能出现的问题,
    //下一个线程拿到当前线程未初始化完毕的引用,造成引用溢出等
    private static volatile Singleton4 instance;

    //2.私有化构造器,不允许通过new创建其他对象
    private Singleton4(){
    }

    //3.提供公共的静态方法,对外暴露实例,注意点:
    //上方的实例只是声明,在方法内部对什么进行实例化
    //当有调用到该方法时才进行实例化
    public static Singleton4 getInstance(){
        //如果声明的instance等于null时才去new
        //否则说明已经创建过了直接返回即可
        if( null == instance){

            //4.使用synchronized对创建实例的代码加锁,同一时间内只保证一个线程执行
            //注意点: 如果锁加在这个位置,上面声明的instance需要使用volatile修饰
            //虽然volatile能设置线程可见性,但并不能保证线程安全
            synchronized (Singleton4.class){
                //业务代码.....
                //5.在锁中再次进行if判断
                if( null == instance){
                	return new Singleton4();
                }
            }
        }
        return  instance;
    }
}

3. 懒汉式二(静态内部类式)

原理及优点

  1. 通过内部类不会随着外部类的装载而装载,解决了饿汉式的内存浪费
  2. 通过静态数据类装载机制,在jvm中只有一份,存在静态域中,解决了懒汉式可能存在的线程安全问题

实现步骤

  1. 创建需要单例的类
  2. 私有化构造器,防止外部new创建实例
  3. 在该类中创建一个私有化静态内部类
  4. 静态内部类中创建私有化的静态的外部类实例
  5. 外部类中提供公共的方法,该方法通过静态内部类返回外部类的实例

代码示例

//懒汉式: 静态内部类式
class Singleton5{
    //1.私有化构造器,不允许通过new创建其他对象
    private Singleton5(){
    }

    //2.创建私有化静态内部类
    private static class InnerSingleton{
        //3.在内部类中创建私有化的静态的外部类实例
        private static Singleton5 INSTANCE = new Singleton5();
    }

    //3.外部类中提供公共的静态方法,在方法中通过内部类获取该类的实例并返回
    public static Singleton5 getInstance(){
        return InnerSingleton.INSTANCE;
    }
}

4. 通过枚举实现单例模式

原理

网上找的资料慢的不是太明白,不瞎写了,通过枚举的什么特点,解决线程安全问题,保证单例问题,先贴一个示例代码吧

代码示例

//枚举单例模式使用示例
public class SingletonTest {
    public static void main(String[] args) {
        // 只能通过INSTANCE获取单例对象
        SharedInstanceEnum demo = SharedInstanceEnum.INSTANCE;

        // 单例对象的成员变量赋值
        SharedInstanceEnum.INSTANCE.val2 = 5;
        SharedInstanceEnum.INSTANCE.config("AAA");

        // 单例对象的方法调用
        SharedInstanceEnum.INSTANCE.getNameString();
        System.out.println(SharedInstanceEnum.INSTANCE.name());
        System.out.println(SharedInstanceEnum.INSTANCE.valueOf("INSTANCE"));
        System.out.println(SharedInstanceEnum.INSTANCE.val2);
        demo.test();
    }
}

//通过枚举解决单例模式
 enum SharedInstanceEnum{
    //这个就是单例的实例对象
    INSTANCE; 
    //成员变量
    private String val1;
    Integer val2;
    //赋值方法
    public void config(String val1) {
        this.val1 = val1;
    }
    //获取值方法
    public String getNameString() {
        return val1;
    }
    //业务代码方法
    public void test(){
        System.out.println(val1+val2);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值