单例模式

来源:互联网 发布:node.js实战 pdf 下载 编辑:程序博客网 时间:2024/06/08 01:48

什么是单例模式?

单例模式,就是保证一个类在程序中只有唯一的一个实例。

单例模式的应用场景?

使用单例模式设计的类,一般充当资源管理器的角色,防止多个线程对共享资源的多重占用。又或者是需要频繁创建、销毁对象的类,设计为单例模式可以减少系统消耗,提高系统性能。比如说数据库连接池和多线程线程池就采用了单例设计模式,还有网页的计时器等。

怎么创建一个单例?

首先我们要解决以下问题:

  1. 类只能被实例化一次
  2. 类的唯一实例能被其他对象获取

我们把类的实例化交给类自己去完成,用 private 修饰构造函数,在类外部就不能通过 new 实例化新的对象了,这就解决了第一个问题;然后提供一个公共的静态方法 getInstance() 来获取类的唯一实例,这就解决了第二个问题。

创建单例的方法一般有:懒汉式、饿汉式、静态内部类

  • 懒汉式(双重检验锁)
    只有当对象第一次被调用时才创建实例。
public class Singleton{    private volatile static Singleton instance;    private Singleton(){}    // 简单地把getInstance()方法设为synchronized也能保证线程安全,但不高效    public static Singleton getInstance(){        if(instance == null){ // 第一次检验实例是否已创建            synchronized(Singleton.class){                if(instance == null){  // 第二次检验防止第一次也检验到null的另一个线程获得锁后又创建第二个实例                    instance = new Singleton();                }            }        }        return instance;    }}

把 instance 声明为 volatile 是因为 new Singleton() 语句不是原子操作,JVM会进行以下三个操作

  1. 为 instance 分配内存
  2. 调用构造函数来初始化成员变量
  3. 将 instance对象 指向分配的内存空间

但即时编译器的指令优化不保证 2 和 3 的执行顺序,可能导致按 1-3-2 的顺序执行时,线程一执行完 3 就被线程二抢占,此时 instance 虽然不为 null 却还没初始化。

  • 饿汉式(static final filed)
    类加载时就初始化 instance 实例。
public class Singleton{    private static final instance = new Singleton();    private Singleton(){}    public static Singleton getInstance(){        return instance;    }}

这个方法非常简单方便,单例一开始就初始化为final,保证了线程安全。但这不是懒加载模式,即使 getInstance() 从没被调用过,单例也会在类加载时就被初始化。饿汉式不适用于 Singleton 实例的创建需要依赖参数或配置文件的场景。

  • 静态内部类(static nested class)
    在 Singleton 内声明一个静态的内部类,让内部类持有 Singleton 单例。
public class Singleton {      private static class SingletonHolder {          private static final Singleton INSTANCE = new Singleton();      }    private Singleton (){}    public static final Singleton getInstance() {          return SingletonHolder.INSTANCE;     }  }

当 getInstance() 方法被调用时,内部类 SingletonHolder 才被加载,然后初始化 INSTANCE 单例,因此是懒汉式,且线程安全的。

总结

一般情况下使用 饿汉式 就好,如果需要懒加载就用使用 静态内部类 的方法吧。

原创粉丝点击