设计模式之——单例模式

本文详细介绍了Java中的单例模式,包括8种实现方式(饿汉式、懒汉式、双重检查锁定等)及其优缺点。其中,静态内部类和枚举实现被认为是最佳实践。单例模式能保证内存中只有一个实例,减少资源占用,但也存在扩展困难和并发调试问题。
摘要由CSDN通过智能技术生成

单例模式(Singleton)

定义: 单例(Singleton)模式指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

应用场景:只需要一个实例的类

        单例模式一共有8中写法,其中只有两种是严格意义上的完美无缺。但实际开发中我们会根据开发需要来使用不同的方法。那么二话不说上代码,分别介绍单例模式的8种实现方式:

1. 饿汉式:

类加载到内存后,就实例化一个单例。

特       点:简单实用,推荐!JVM保证线程安全。
唯一缺点:不管用到与否,类加载时就完成实例化(浪费!)

代码:

public class Singleton01 {
    private static final Singleton01 INSTANCE = new Singleton01();
    private Singleton01(){}//最关键之处
    public static Singleton01 getInstance(){
        return INSTANCE;
    }
}

        首先通过private Singleton01(){}创建私有化构造方法,别人想要访问只能通过getInstance(),返回结果永远是INSTANCE的实例。调用方式:

public static void main(String[] args) {
        Singleton01 s = Singleton01.getInstance();
    }

2. 饿汉式优化:

此方式对上一种方式做了简单规范优化,将INSTANCE放入静态块中初始化,与上一种本质上并无区别。

代码:

public class Singleton01 {
    private static final Singleton01 INSTANCE;
    static {
        INSTANCE = new Singleton01();
    }
    private Singleton01(){}
    public static Singleton01 getInstance(){
        return INSTANCE;
    }
}

3. 懒汉式(lazy loading)

优 点:达到按需初始化的目的
缺 点:线程不安全

public class Singleton03 {
    private static volatile Singleton03 INSTANCE;
    private Singleton03(){}
    public static Singleton03 getInstance(){
        if (INSTANCE == null) {
            INSTANCE = new Singleton03();
        }
        return INSTANCE;
    }
}

        在需要调用getInstance()方法时对INSTANCE进行初始化,但必须加上volatile,否则在多线程访问下可能出现实例不唯一的情况。

4. 加锁(synchronized)

此方式仅在"public staticSingleton04 getInstance() {}"这句上加了synchronized,其余代码与上条无异。
优 点:线程安全
缺 点:占用内存量大,效率低

代码(与上一条不同处):

public static synchronized Singleton04 getInstance()

        通过对getInstance()加锁就可以解决上面说的线程安全问题。但同时因为加的是静态锁,使内存使用量大,效率降低。

5. 优化锁

此方法将锁放入方法中,通过判断INSTANCE未被加载时加锁进行初始化。

代码(与上一条不同处):

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

        通过减少同步代码块的方式提高效率,但为解决多线程访问会出现实例不唯一的情况。

6. 双重判断

根据之前遇到的问题进行总结,对加锁后的单例进行再次判断线程安全后进行初始化。
优 点:完美的解决了以上的所有问题。
缺 点:没有。(除非你觉得写得比较复杂)

代码(与上一条不同处):

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

        此方法是单例模式中的最完美的写法之一,并在此基础上衍生了以下两种写法。

7. 静态内部类

在第一种方式的基础上改用静态内部类方式
优 点:JVM保证线程安全同时实现懒加载

代码:

public class Singleton07 {
    private Singleton07(){}    
    private static class S07Holder {
        private final static Singleton07 INSTANCE = new Singleton07();
    }
    public static Singleton07 getInstance(){
        return S07Holder.INSTANCE;
    }
}

        此方法也是单例模式种最完美的实现方式之一,使用静态内部类加载外部类时不会加载内部类,实现了懒加载。

8. 枚举单例

Java的创始人之一Joshua Bloch曾写过一本书《Effective Java》,其中他使用枚举(enum)来实现单例模式。

代码:(仅此而已。。)

public enum Singleton08 {
    INSTANCE;
}

        完美中的完美(大神就是大神)。。通过枚举类型解决了以上出现的所有问题。使用时直接通过Singleton08.INSTANCE调用。

关于JVM为什么能保证线程安全请参考我的文章:Java复习之-JVM概述JVM学习之——类加载

单例模式的优点和缺点

单例模式的优点:

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

单例模式的缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

单例模式看起来非常简单,实现起来也非常简单。单例模式在面试中是一个高频面试题。希望大家能够认真学习,掌握单例模式,提升核心竞争力,给面试加分,顺利拿到 Offer。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值