开发者社区> 1717204071597443> 正文

设计模式 - 创建型模式_7种单例模式实现

简介: 单例模式可以说是整个设计中最简单的模式之⼀,在编程开发中经常会遇到这样⼀种场景,那就是需要保证⼀个类只有⼀个实例哪怕多线程同时访问,并需要提供⼀个全局访问此实例的点。 单例模式主要解决的是,⼀个全局使⽤的类频繁的创建和消费,从⽽提升提升整体的代码的性能。
+关注继续查看

@[toc]

在这里插入图片描述


创建型模式

创建型模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤性。

| 类型| 实现要点 |
|--|--|
| 工厂方法 | 定义⼀个创建对象的接⼝,让其⼦类⾃⼰决定实例化哪⼀个⼯⼚类,⼯⼚模式使其创建过程延迟到⼦类进⾏。 |
| 抽象工厂| 提供⼀个创建⼀系列相关或相互依赖对象的接⼝,⽽⽆需指定它们具体的类。 |
|建造者 |将⼀个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示 |
| 原型 | ⽤原型实例指定创建对象的种类,并且通过拷⻉这些原型创建新的对象。 |
| 单例| 保证⼀个类仅有⼀个实例,并提供⼀个访问它的全局访问点。 |


概述

在设计模式中按照不同的处理⽅式共包含三⼤类:

  • 创建型模式
  • 结构型模式
  • ⾏为模式

其中创建型模式⽬前已经搞了其中的四个:

  • ⼯⼚⽅法模式
  • 抽象⼯⼚模式
  • 建造者模式
  • 原型模式
  • 除此之外还有最后⼀个 单例模式

在这里插入图片描述
单例模式可以说是整个设计中最简单的模式之⼀,在编程开发中经常会遇到这样⼀种场景,那就是需要保证⼀个类只有⼀个实例哪怕多线程同时访问,并需要提供⼀个全局访问此实例的点。

单例模式主要解决的是,⼀个全局使⽤的类频繁的创建和消费,从⽽提升提升整体的代码的性能。


Case

⽇常开发所能⻅到的,例如:

  1. 数据库的连接池不会反复创建
  2. spring中⼀个单例模式bean的⽣成和使⽤
  3. 在我们平常的代码中需要设置全局的的⼀些属性保存

在我们的⽇常开发中⼤致上会出现如上这些场景中使⽤到单例模式,虽然单例模式并不复杂但是使⽤⾯却⽐较⼴。


7种单例模式实现

设计模式 - 创建型模式_ 单例模式 Singleton Pattern

并发编程-09安全发布对象+单例模式详解

单例模式的实现⽅式⽐较多,主要在实现上是否⽀持懒汉模式、是否线程安全中运⽤各项技巧。

当然也有⼀些场景不需要考虑懒加载也就是懒汉模式的情况,会直接使⽤ static 静态类或属性和⽅法的⽅式进⾏处理,供外部调⽤。

那么接下来我们就通过实现不同⽅式的实现单例模式。


静态类使⽤

public class Singleton_00 {

    public static Map<String,String> cache = new ConcurrentHashMap<String, String>();

}
  • 以上这种⽅式在平常的业务开发中⾮常场常⻅,这样静态类的⽅式可以在第⼀次运⾏的时候直接初始化Map类,同时这⾥我们也不需要到延迟加载在使⽤。
  • 在不需要维持任何状态下,仅仅⽤于全局访问,这个使⽤使⽤静态类的⽅式更加⽅便。
  • 但如果需要被继承以及需要维持⼀些特定状态的情况下,就适合使⽤单例模式。

懒汉模式(线程不安全)

public class Singleton_01 {

    private static Singleton_01 instance;

    private Singleton_01() {
    }

    public static Singleton_01 getInstance(){
        if (null != instance) return instance;
        return new Singleton_01();
    }

}
  • 单例模式有⼀个特点就是不允许外部直接创建,也就是 new Singleton_01() ,因此这⾥在默认的构造函数上添加了私有属性 private 。
  • ⽬前此种⽅式的单例确实满⾜了懒加载,但是如果有多个访问者同时去获取对象实例,就会造成多个同样的实例并存,从⽽没有达到单例的要求。

懒汉模式(线程安全)

public class Singleton_02 {

    private static Singleton_02 instance;

    private Singleton_02() {
    }

    public static synchronized Singleton_02 getInstance(){
        if (null != instance) return instance;
        return new Singleton_02();
    }

}

此种模式虽然是安全的,但由于把锁加到⽅法上后,所有的访问都因需要锁占⽤导致资源的浪费。 如果不是特殊情况下,不建议此种⽅式实现单例模式。


饿汉模式(线程安全)

public class Singleton_03 {

    private static Singleton_03 instance = new Singleton_03();

    private Singleton_03() {
    }

    public static Singleton_03 getInstance() {
        return instance;
    }

}
  • 此种⽅式与我们开头的第⼀个实例化 Map 基本⼀致,在程序启动的时候直接运⾏加载,后续有外部需要使⽤的时候获取即可。
  • 但此种⽅式并不是懒加载,也就是说⽆论程序中是否⽤到这样的类都会在程序启动之初进⾏创建。

使⽤类的内部类(线程安全)

public class Singleton_04 {

    private static class SingletonHolder {
        private static Singleton_04 instance = new Singleton_04();
    }

    private Singleton_04() {
    }

    public static Singleton_04 getInstance() {
        return SingletonHolder.instance;
    }

}
  • 使⽤类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的⽅式耗费性能。
  • 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是⼀个类的构造⽅法在多线程环境下可以被正确的加载。
  • 此种⽅式也是⾮常推荐使⽤的⼀种单例模式

双重锁校验(线程安全)

public class Singleton_05 {

    private static Singleton_05 instance;

    private Singleton_05() {
    }

    public static Singleton_05 getInstance(){
       if(null != instance) return instance;
       synchronized (Singleton_05.class){
           if (null == instance){
               instance = new Singleton_05();
           }
       }
       return instance;
    }

}
  • 双重锁的⽅式是⽅法级锁的优化,减少了部分获取实例的耗时。
  • 同时这种⽅式也满⾜了懒加载。

CAS「AtomicReference」(线程安全)

import java.util.concurrent.atomic.AtomicReference;

public class Singleton_06 {

    private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

    private static Singleton_06 instance;

    private Singleton_06() {
    }

    public static final Singleton_06 getInstance() {
        for (; ; ) {
            Singleton_06 instance = INSTANCE.get();
            if (null != instance) return instance;
            INSTANCE.compareAndSet(null, new Singleton_06());
            return INSTANCE.get();
        }
    }

    public static void main(String[] args) {
        System.out.println(Singleton_06.getInstance());  
        System.out.println(Singleton_06.getInstance()); 
    }


}
  • java并发库提供了很多原⼦类来⽀持并发访问的数据安全性 AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference
  • AtomicReference 可以封装引⽤⼀个V实例,⽀持并发访问如上的单例⽅式就是使⽤了这样的⼀个特点。
  • 使⽤CAS的好处就是不需要使⽤传统的加锁⽅式保证线程安全,⽽是依赖于CAS的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外的开销,并且可以⽀持较⼤的并发性。
  • 当然CAS也有⼀个缺点就是忙等,如果⼀直没有获取到将会处于死循环中。

Effective Java作者推荐的枚举单例(线程安全)


public enum Singleton_07 {

    INSTANCE;
    public void test(){
        System.out.println("hi~");
    }

}
约书亚·布洛克(英语:Joshua J. Bloch,1961年8⽉28⽇-),美国著名程序员。他为Java平台设计并实作了许多的功能,曾担任Google的⾸席Java架构师(Chief Java Architect)。
  • Effective Java 作者推荐使⽤枚举的⽅式解决单例模式,此种⽅式可能是平时最少⽤到的。
  • 这种⽅式解决了最主要的: 线程安全、⾃由串⾏化、单⼀实例。

调⽤⽅式:

 @Test
    public void test() {
        Singleton_07.INSTANCE.test();
    }
  • 这种写法在功能上与共有域⽅法相近,但是它更简洁,⽆偿地提供了串⾏化机制,绝对防⽌对此实例化,即使是在⾯对复杂的串⾏化或者反射攻击的时候。虽然这中⽅法还没有⼴泛采⽤,但是单元素的枚举类型已经成为实现Singleton的最佳⽅法。
  • 但此种⽅式在存在继承场景下是不可⽤的。

小结

这里我们看到了懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串⾏化等等各种技能。

在平时的开发中如果可以确保此类是全局可⽤不需要做懒加载,那么直接创建并给外部调⽤即可。

但如果是很多的类,有些需要在⽤户触发⼀定的条件后才显示,那么⼀定要⽤懒加载。线程的安全上可以按需选择。

在这里插入图片描述

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【设计模式】-创建型模式-第2章第1讲-【单例模式】
【设计模式】-创建型模式-第2章第1讲-【单例模式】
17 0
【设计模式】Java设计模式 - 单例模式
单例模式,是java设计模式中最简单的设计模式,是属于创建类型模式。单例模式就是只能有一个实例,即一个类有且仅有一个实例,并且自行实例化向整个系统提供。
37 0
创建型设计模式——单例模式
创建型设计模式——单例模式
97 0
设计模式系列5 - 单例模式
我理解单例应该是所有模式中,最简单,也是使用最多的一种,我就简单总结一下,首先了解一下单例模式的定义。
46 0
设计模式-单例模式
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此有些设计大师并把把其称为设计模式之一。
55 0
【设计模式】单例模式
端午小长假冷不丁的就完了,那么接着品设计模式,今天要介绍的设计模式是单例模式,单例模式在编程中的使用很广泛,如线程池、缓存等都可采用单例模式来保证整个系统只有一个实例。然而,如何保证只有唯一一个实例是值得探讨的问题,下面开始介绍单例模式。
46 0
设计模式|单例模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
43 0
设计模式—单例模式
设计模式—单例模式
69 0
设计模式 -创建型模式之单例模式的五种实现
设计模式 -创建型模式之单例模式的五种实现
62 0
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载