单例模式的8种写法

简介: 单例模式的8种写法

1、饿汉式,线程安全

public class Singleton {

    /**
     * 优点:写法简单,类装载的时候完成实例化,线程安全
     * 缺点:类装载的时候就完成实例化,没有Lazy Loading,如果没有被使用,会造成内存浪费
     */
    private static final Singleton INSTANCE = new Singleton();

    public static Singleton getInstance(){
        return INSTANCE;
    }

}

2、饿汉式,线程安全

public class Singleton {

    /**
     * 与第一种方式类似,只是将实例化过程放在静态块中。不知道算不算一种写法。
     * 优点:写法简单,类装载的时候完成实例化,线程安全
     * 缺点:类装载的时候就完成实例化,没有Lazy Loading,如果没有被使用,会造成内存浪费
     */
    private static Singleton INSTANCE;
    
    static{
        if(INSTANCE == null){
            INSTANCE = new Singleton();
        }
    }
    
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

3、懒汉式,线程不安全

public class Singleton {

    /**
     * 优点:Lazy Loading。避免多余的内存开销。
     * 缺点:线程不安全,多线程环境下容易产生多个实例。
     */
    private static Singleton INSTANCE;

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

4、懒汉式,线程安全(同步方法)

public class Singleton {

    /**
     * 优点:Lazy Loading。避免多余的内存开销。synchronized线程同步,保证了线程安全。
     * 缺点:synchronized将方法串行化,效率太低
     */
    private static Singleton INSTANCE;

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

5、懒汉式,线程安全(同步代码块)

public class Singleton {

    /**
     * 优点:Lazy Loading。避免多余的内存开销。改良同步方法效率低的问题。
     * 缺点:synchronized同步代码块,无法保证线程同步。如果,线程A在判断INSTANCE == null还未执行完毕,另一个线程已经return出一个实例,那么将会产生多个实例。
     */
    private static Singleton INSTANCE;

    public static Singleton getInstance(){
        if(INSTANCE == null){
            synchronized(Singleton.class){
                INSTANCE = new Singleton();
            }
        }
        return INSTANCE;
    }
}

6、懒汉式,线程安全(双重检查)

public class Singleton {

    /**
     * 优点:线程安全,Lazy Loading,效率高
     * 缺点:使用volatile,jdk1.5版本之前无法完全避免重排序锁导致的问题。jdk1.5之前无法保证线程安全。(现在都java12了, 问题不大)
     */
    private static volatile Singleton INSTANCE;

    public static Singleton getInstance(){
        if(INSTANCE == null){
            synchronized(Singleton.class){
                if(INSTANCE == null){
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

7、静态内部类,线程安全(推荐使用)

public class Singleton {

    /**
     * 优点:Lazy Loading,线程安全,写法简单
     * 缺点:需要额外处理序列化问题,否则一个序列化的对象实例在每次反序列化后都会创建一个新的实例;传参复杂。
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

8、枚举,线程安全(推荐使用)

public enum  Singleton {


    /**
     * 线程安全,避免反序列化问题。推荐使用
     */
    INSTANCE;
    public void getInstance(){

    }

//    public static void main(String[] args) {
//        Singleton.INSTANCE.getInstance();
//    }
}

为什么说枚举能保证线程安全,而且能保证单例?

通过javap反编译Singleton枚举。

INSTANCE最终被声明为static final。

JVM类加载过程中,虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行()方法后,其他线程唤醒之后不会再次进入()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。

所以枚举既保证了线程的安全性,又保证了实例的单例性。

总结:单例模式使用哪种写法,最终取决于业务的需要(如是否需要频繁的创建或者销毁对象)。推荐静态内部类和枚举两种写法。

相关文章
|
关系型数据库 MySQL OLAP
TiDB亿级数据亚秒响应查询方案介绍
TiDB亿级数据亚秒响应查询方案介绍
1048 0
|
存储 安全 Java
jdk21的外部函数和内存API(MemorySegment)(官方翻译)
本文介绍了JDK 21中引入的外部函数和内存API(MemorySegment),这些API使得Java程序能够更安全、高效地与JVM外部的代码和数据进行互操作,包括调用外部函数、访问外部内存,以及使用不同的Arena竞技场来分配和管理MemorySegment。
485 1
jdk21的外部函数和内存API(MemorySegment)(官方翻译)
|
JavaScript
Vue组件传值异步问题--子组件拿到数据较慢
Vue组件传值异步问题--子组件拿到数据较慢
642 154
|
11月前
|
前端开发 UED 开发者
精通 CSS 阴影效果:从基础到高级应用
本文详细介绍了CSS阴影效果的使用方法,包括`box-shadow`和`text-shadow`的基本语法、参数解释及进阶应用。通过多个示例展示了如何实现外阴影、内阴影、渐变阴影以及多重阴影效果,并结合实际场景如浮动按钮和卡片式设计,说明了阴影与背景的综合应用。此外,还提供了性能优化建议,帮助开发者在确保视觉效果的同时提升页面性能。最后,总结了CSS阴影的重要性及其对网页美观度和用户体验的提升作用。
1193 6
|
机器学习/深度学习 搜索推荐 算法
云上智能推荐:重塑信息获取与消费的未来
市场竞争与合规性:随着云上智能推荐市场的不断扩大,市场竞争也日益激烈。如何在激烈的市场竞争中脱颖而出,同时遵守相关法律法规和行业标准,是系统开发者需要面对的重要问题。
|
Java 调度
java 中sleep 注意点
java 中sleep 注意点
|
存储 Web App开发 运维
发布、部署,傻傻分不清楚?从概念到实际场景,再到工具应用,一篇文章让你彻底搞清楚
部署和发布是软件工程中经常互换使用的两个术语,甚至感觉是等价的。然而,它们是不同的! • 部署是将软件从一个受控环境转移到另一个受控环境,它的目的是将软件从开发状态转化为生产状态,使得软件可以为用户提供服务。 • 发布是将软件推向用户的过程,应用程序需要多次更新、安全补丁和代码更改,跨平台和环境部署需要对版本进行适当的管理,有一定的计划性和管控因素。
4565 1
|
缓存 负载均衡 应用服务中间件
nginx.conf 配置解析及常用配置
nginx.conf 配置解析及常用配置
406 6
|
存储 运维 Shell
Ansible自动化运维工具安装和基本使用
Ansible 是一款无代理的IT自动化工具,通过SSH连接目标主机执行配置管理、应用部署和云端管理任务。它使用YAML编写的Playbook定义任务,核心组件包括Playbook、模块、主机清单、变量等。Ansible的优势在于易用、功能强大、无须在目标主机安装额外软件,并且开源。安装过程涉及配置网络源、yum安装和SSH密钥设置。通过定义主机清单和使用模块进行通信测试,确保连接成功。
493 2
Ansible自动化运维工具安装和基本使用
何处理Java中的ZipException异常?
何处理Java中的ZipException异常?