首页 > 其他分享 >单例模式8种写法

单例模式8种写法

时间:2023-06-03 11:58:33浏览次数:35  
标签:模式 Singleton6 INSTANCE static private 单例 线程 写法 public

0. 为什么需要单例模式?

  • 节省内存和计算
  • 保证结果正确
  • 方便管理

使用场景:

1. 饿汉式(静态常量)—推荐指数:★★☆☆☆

优点:不会有线程安全问题。
缺点:在类加载的时候就创建对象,如果一直没使用到该对象的话,就造成了内存浪费,如果对象初始化的工作有很多,也会影响到性能。

代码展示:

//饿汉式(静态常量) ---可用
public class Singleton1 {
    // 类加载时就创建该实例
    private final static Singleton1 INSTANCE = new Singleton1();

    private Singleton1(){}

    public static Singleton1 getInstance(){
        return INSTANCE;
    }
}

2. 饿汉式(静态代码块) —推荐指数:★★☆☆☆

优缺点和第一种方式基本一致。

//饿汉式(静态代码块) ---可用
public class Singleton2 {
    private final static Singleton2 INSTANCE;
    // 以静态代码快的形式创建实例
    static {
        INSTANCE = new Singleton2();
    }

    private Singleton2(){}

    public static Singleton2 getInstance(){
        return INSTANCE;
    }
}

3. 懒汉式(线程不安全)—推荐指数:☆☆☆☆☆

优点:使用到的时候才会创建对象,不会造成各种资源浪费问题。
缺点:有线程安全问题。

//懒汉试(线程不安全)---不可用
public class Singleton3 {

    private static Singleton3 INSTANCE;

    private Singleton3(){}

    public static Singleton3 getInstance(){
        if (INSTANCE == null){
            // 当多个线程同时执行到这里,就会创建多个实例,那各自线程的实例就不是同一个了
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }
}

4. 懒汉式(线程安全,同步方法)—推荐指数:★☆☆☆☆

优点:使用到的时候才会创建对象,不会造成各种资源浪费问题。
缺点:没有线程安全问题,但是有很大的性能问题,当多个线程同时到达getInstance()方法时,需要排队进入。

这个是在第 3 步的基础上实现的,使用 synchronized 修饰静态方法,由于加上了同步工具类,同一时间只能有一个线程操作,也使得性能下降,所以也不推荐使用:

//懒汉试(线程安全)---不推荐
public class Singleton4 {

    private static Singleton4 INSTANCE;

    private Singleton4(){}

    public synchronized static Singleton4 getInstance(){
        if (INSTANCE == null){
            INSTANCE = new Singleton4();
        }
        return INSTANCE;
    }
}

5. 懒汉式(线程不安全,同步代码块)—推荐指数:☆☆☆☆☆

优点:使用到的时候才会创建对象,不会造成各种资源浪费问题。
缺点:线程不安全,还有性能问题!多个线程在synchronized那一行排队,进入代码块后一样会创建多个对象。

这个是在第 4 步的基础上作进一步尝试,虽然性能上的问题解决了,但是又出现了线程不安全的问题,如下:

//懒汉试(线程不安全,同步代码块)---不可用
public class Singleton5 {

    private static Singleton5 INSTANCE;

    private Singleton5(){}

    public static Singleton5 getInstance(){
        if (INSTANCE == null){
            // 如果多个线程同时执行到这里,依然会创建多个实例
            synchronized (Singleton5.class){
                INSTANCE = new Singleton5();
            }
        }
        return INSTANCE;
    }
}

6. 懒汉式(双重检查)—推荐指数:★★★★☆

优点:使用到的时候才会创建对象,不会造成各种资源浪费问题。
缺点:复杂。

//双重检查---推荐使用
public class Singleton6 {

    private static Singleton6 INSTANCE;

    private Singleton6(){}

    public static Singleton6 getInstance(){
        if (INSTANCE == null){
            synchronized (Singleton6.class){
                //再做一次检查:由于同时只能有一个线程进入到这里,所以此时的 INSTANCE 如果还是 null,那么就再创建,创建之后有且仅有这一个实例
                if (INSTANCE == null){
                    INSTANCE = new Singleton6();
                }
            }
        }
        return INSTANCE;
    }
}

这个优化我们利用了双重检测机制和同步锁,这种方式也称为双重同步锁单例模式,但是这个案例还是线程不安全的,大家通过代码层面的分析后,发现确实不会有线程安全问题,那问题出现在哪呢?这个其实要和对象创建步骤和JVM 指令重排挂钩,我们正常创建对象的指令步骤是这样的:

  • memory = allocate() 分配对象的内存空间
  • ctorInstance() 初始化对象,执行对应的构造方法
  • instance = memory 设置instance指向刚分配的内存

但是因为JVM和cpu优化,发生了指令重排,执行顺序如下:

  • memory = allocate() 分配对象的内存空间
  • instance = memory 设置instance指向刚分配的内存
  • ctorInstance() 初始化对象

我们可以结合代码,假如A线程进入同步代码块执行 instance = new Singleton6(),执行到“instance = memory 设置instance指向刚分配的内存”,这个时候B线程在第一次执行“if (instance == null)”,发现instance不为空,直接返回instance实例,其实线程B得到的这个实例并没有完全初始化(A还没有执行完对象的初始化步骤)就已经使用了。

那如何禁止指令重排呢,很简单,用我们前面文章提到的volatile关键字就可以了

在 INSTANCE 前加上 volatile 关键字来修饰,代码如下:

//双重检查---推荐使用
public class Singleton6 {

    private volatile static Singleton6 INSTANCE;

    private Singleton6(){}

    public static Singleton6 getInstance(){
        if (INSTANCE == null){
            synchronized (Singleton6.class){
                //再做一次检查:由于同时只能有一个线程进入到这里,所以此时的 INSTANCE 如果还是 null,那么就再创建,创建之后有且仅有这一个实例
                if (INSTANCE == null){
                    INSTANCE = new Singleton6();
                }
            }
        }
        return INSTANCE;
    }
}

7. 静态内部类 — 推荐指数:★★★☆☆

优点:使用到的时候才会创建对象,不会造成各种资源浪费问题,线程安全。
缺点:没有太大缺点。

由于类在初始化时,并不会初始化静态内部类中的实例,所以这属于饿汉式单例:

//静态内部类 --- 推荐使用
public class Singleton7 {

    private Singleton7(){}

    private static class SingletonInstance{
        //JVM会保证构造方法的线程安全问题,即使多个线程同时访问 getInstance() 方法,也只会创建一个实例,这是 JVM 保证的 
        private static final Singleton7 INSTANCE = new Singleton7();
    }

    public static Singleton7 getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

8. 枚举 — 推荐指数:★★★★★

优点:使用到的时候才会创建对象,不会造成各种资源浪费问题,线程安全。
缺点:最优方案。

//枚举 --- 生产实践推荐使用
public enum Singleton8 {
    INSTANCE;

    //方法
    public void whatever(){ }
}

使用的时候只需要调用Singleton8.INSTANCE ,比如这里想调用该类中的 whatever()方法,只需要执行 Singleton8.INSTANCE.whatever()

文章来源:单例模式8种写法

个人微信:CaiBaoDeCai

微信公众号名称:Java知者

微信公众号 ID: JavaZhiZhe

谢谢关注!

标签:模式,Singleton6,INSTANCE,static,private,单例,线程,写法,public
From: https://www.cnblogs.com/javazhizhe/p/17453748.html

相关文章

  • 单例模式的运用
    目录一、介绍二、饿汉式2.1静态变量方式2.2静态代码块方式2.3枚举方式三、懒汉式3.1线程不安全方式3.2线程安全方式3.3双重检查锁方式3.4静态内部类方式四、破坏单例模式4.1序列化破坏4.2序列化破坏解决办法4.3反射破坏4.4反射破坏解决办法一、介绍单例模式:属于创建......
  • android-夜间模式
    资源1AndroidMaterialDesign系列之夜间模式阐述了夜间模式的资源文件,告知建立了values-night文件夹对于夜间模式的颜色和主题配置,我们需要建立一个res下建立一个values-night文件夹,里面放着夜间主题样式的color等资源。colors.xml配置如下:<?xmlversion="1.0"encoding="utf-8"......
  • 移动开发之设计模式-组合模式(IOS&Android)
    组合模式组合模式(CompositePattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相......
  • java单例模式几种实现方式
    1、饿汉式(线程安全,调用效率高,但是不能延时加载):publicclassImageLoader{privatestaticImageLoaderinstance=newImageLoader;privateImageLoader(){}publicstaticImageLoadergetInstance(){returninstance;}}一上来就把单例对象创建出来了,要用的时候直......
  • Sentinel规则Pull模式持久化
    阅读文本大概需要3分钟。   前一篇【使用Nacos存储Sentinel的限流规则】讲了基于Nacos的Push模式持久化,这里讲下基于本地文件的Pull模式持久化。在网上看到一篇讲这个讲得不错的:从官网的说明https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel#Pull模式有......
  • TS中一些常见报错的写法修正
    Cannotinvokeanobjectwhichispossibly‘undefined‘在openSpeedUpModal方法后面加上!openSpeedUpModal!(record.id,record.priority)参考:https://flowus.cn/46989933-affc-40a1-b9ab-76c72b061381#1db40beb-601f-4054-8b39-1ef64f36839c......
  • Golang - 选项模式 vs 构建器模式
    在使用Golang创建复杂对象时,常用的两种模式是选项模式(Optionspattern)和构建器模式(Builderpattern)。这两种模式各有优缺点,选择适合项目需求的模式取决于具体情况。问题假设我们想创建一个具有许多可选参数的复杂对象。一种方法是创建一个构造函数,该构造函数接受所有参数,并为可......
  • 方芳:坡面尺度上混合植被恢复模式对土壤修复的影响
    武汉市江夏区交通局武汉市江夏区公路局  武汉市江夏区公路建筑工程公司武汉市江夏城投集团有限公司武汉江夏路桥工程总公司 武汉工程大学 土木工程与建筑学院    方芳    15927602711坡面尺度上混合植被恢复模式对土壤修复的影响摘要:随着人类活动......
  • 工厂模式配置servlet(servlet升级版)
    1、创建一个类点击查看代码packagecom.bh.controller;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;impo......
  • 设计模式
    设计模式介绍设计模式是程序员在面对同类软件工程设计问题所总结出来的有用经验,是某类问题的通用解决方案。作用:使程序(软件)具有更好:代码重用性(即相同功能的代码,不用多次编写)可读性(即编程规范性,便于其他程序员的阅读和理解)可扩展性(即:当需要增加新的功能时,非常的方便,称为可维......