单例模式(创建型模式)

目录

1.什么是单例模式        

2.介绍

3.代码实现

饿汉式

懒汉模式,线程不安全

懒汉模式,线程安全

双重检查机制

指令重排问题:


1.什么是单例模式        

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

2.介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

优点:

  • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

3.代码实现

饿汉式

主要利用类加载到内存后,就实例化一个单例,JVM保证线程安全
简单实用,推荐实用!
唯一缺点:不管用到与否,类装载时就完成实例化,导致内存资源的消耗。

public class Singleton {
​
    // 1. 将类的构造函数私有化,避免外界使用构造函数创建实例
    private Singleton() { }
​
    // 2. 定义一个私有的静态实例对象,并进行初始化
    private static Singleton instance = new Singleton();
​
    // 3. 提供一个公有的获取实例对象的静态方法
    public static Singleton getInstance(){
        return instance;
    }
}

懒汉模式,线程不安全

虽然达到了延迟加载初始化的目的,但却带来线程不安全的问题。

public class Singleton {
​
    // 1. 将类的构造函数私有化,避免外界使用构造函数创建实例
    private Singleton() { }
​
    // 2. 定义声明一个私有的静态实例对象,并没有初始化对象
    private static Singleton instance;
​
    // 3. 提供一个公有的获取实例对象的静态方法,在获取时来初始化实例对象
    public static Singleton getInstance(){
        // 懒汉式是在得到本类对象时,才会创建该对象
​
        // 如果不进行判断,每次都会创建一个新的对象
        // 所以加一个判断,判断 instance 是否为 null, 如果为null,就需要创建新的对象
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式,第一种实现方式,在调用getInstance()方法获取时,Singleton类的对象的时候才开始初始化Singleton类的对象,这样就实现了懒加载的效果,但是只能在单线程下使用;而如果在多线程情况下,就会出现线程安全问题。

如一个线程进入了if(instance == null )判断语句,还未来得及往下执行初始化操作,另一个线程也通过了这个判断语句,这时便会产生多个实例,所以在多线程环境下不可使用这种方式。

懒汉模式,线程安全

虽然达到了线程安全的目的,但是在方法上加同步锁后,导致效率降低。

public class Singleton {

    // 1. 将类的构造函数私有化,避免外界使用构造函数创建实例
    private Singleton() { }

    // 2. 定义声明一个私有的静态实例对象,并没有初始化对象
    private static Singleton instance;

    // 3. 提供一个公有的获取实例对象的静态方法,在获取时来初始化实例对象,并在方法上加上同步锁关键字synchronized。
    public static synchronized Singleton getInstance(){
        // 懒汉式是在得到本类对象时,才会创建该对象

        // 如果不进行判断,每次都会创建一个新的对象
        // 所以加一个判断,判断 instance 是否为 null, 如果为null,就需要创建新的对象
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式,第二种实现方式,实现了懒加载的效果,同时也解决了多线程安全问题,但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。

但是我们可以看出,每次获取实例都要进行同步(加锁),因此效率较低,并且可能很多同步都是没必要的。

双重检查机制

public class Singleton {

    // 1. 将类的构造函数私有化,避免外界使用构造函数创建实例
    private Singleton() { }

    // 2. 定义声明一个私有的静态实例对象,并没有初始化对象
    // 可能在jvm编译过程中,发生指令重排序问题,导致空指针异常,需要加 volatile关键字
    private static volatile Singleton instance;

    // 3. 提供一个公有的获取实例对象的静态方法,在获取时来初始化实例对象,并在初始化对象时加上同步代码块。
    public static Singleton getInstance(){
        // 懒汉式是在得到本类对象时,才会创建该对象

        // 如果不进行判断,每次都会创建一个新的对象
        // 第一次判断,判断 instance 是否为null, 如果为null,就需要创建新的对象,如果不为null,就不需要抢占锁,直接返回对象
        if(instance == null) {

            // 在此加上同步代码块
            synchronized(Singleton.class){
                // 这里加上第二次判断,避免重复创建新对象。
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

指令重排问题:

在new对象时即 instance = new Singleton(),正常它会发生这三个步骤:

  1. 在JVM的堆中申请一块内存空间
  2. 对象进行初始化操作
  3. 将堆中的内存空间的引用地址赋值给一个引用变量instance。

但是如果没有加上volatile关键字时,可能导致出现的步骤为 1 -> 3 -> 2

这时候就会出现空指针异常啦,所以我们得加在instance变量加上关键字 volatile,确保在初始化变量时不会发送指令重排序问题。

java 单例模式问题? - 知乎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值