单例模式

单例模式

确保一个类只有一个实例,并提供该实例的全局访问点,。

类图

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。

6种实现方式

实现方式总结

懒汉式-线程不安全

私有静态变量 instance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 instance,节约资源。

但多线程环境下是不安全的,如果多线程能够同时进入 if (instance == null) ,且 instance 为 null,那么会有多个线程执行 instance = new Singleton(); 语句,导致多次实例化 instance。

public class Singleton {

  private Singleton instance;

  private Singleton(){

  }

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

饿汉式-线程安全

线程不安全问题主要于 instance 被多次实例化,采取直接实例化 uniqueInstance 的方式线程安全了。

但直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }
    //对外提供获取实例的静态方法
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式-线程安全

只要对 getInstance() 方法加锁,在一个时间点只有一个线程能够进入,就避免了多次实例化的问题。

但当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,性能上有一定的损耗。

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

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

双重校验锁-线程安全

懒汉线程安全方式加锁范围较大,可以只针对实例化部分,且只有 instance 未被实例化时,才加锁。

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

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

双重锁校验是为了避免多线程同时执行第一个 if ,而创建多个实例。

用 volatile 关键字是为了防止指令重排instance = new Singleton(); 这段代码分为三步。

  1. 分配内存空间
  2. 初始化对象
  3. 将 uniqueInstance 指向分配的内存地址

由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1 > 3 > 2,如果是多线程下,可能获得是一个还没有被初始化的实例。

静态内部类实现

当 Singleton 类加载时,静态内部类 SingletonHolder 没被加载进内存。当调用 getInstance() 方法触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。

这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。

public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

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

枚举实现

最佳实践,实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。

public enum Singleton {
    instance;
    Singleton() {
    }
}

优缺点

优点

  • 提供了对唯一实例的访问控制。
  • 由于在系统内存中只存在一个对象,因此可以节约资源。
  • 允许可变数目的实例。可基于单例模式进行扩展,使用相似的方法来获得指定个数的对象实例。

缺点

  • 由于没有抽象层,扩展有很困难。
  • 职责过重,在一定程度上违背了“单一职责原则”。既充当了工厂角色,又充当了产品角色。
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;比如 JVM 的自动垃圾回收的技术,如果实例化的对象长时间不被利用,会自动销毁并回收资源,下次又将重新实例化,将导致对象状态的丢失。

适用场景

在以下情况下可以使用单例模式:

  • 系统只需要一个实例对象,或需要考虑资源消耗问题。
  • 客户调用类的单个实例只允许使用一个公共访问点。
  • 一个系统中要求一个类只有一个实例时。如果一个类可以有几个实例共存,就需要改进,成为多例模式。

应用

  1. java.lang.Runtime;
  2. GUI 中也有一些(java.awt.Toolkit#getDefaultToolkit() java.awt.Desktop#getDesktop())
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇