单例模式

来源:互联网 发布:淘宝不开发票怎么办 编辑:程序博客网 时间:2024/05/20 18:41

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式最主要的特点:

  1. 构造函数不对外开放,一般为private;
  2. 通过一个静态方法或者枚举返回单例类对象;
  3. 确保单例类的对象有且只有一个,尤其是在多线程的环境下;
  4. 确保单例类对象在反序列化时不会重新构建对象。

通过将单例类构造函数私有化,使得客户端不能通过 new 的形式手动构造单例类的对象。单例类会暴露一个共有静态方法,客户端需要条用这个静态方法获取到单例类的唯一对象,在获取到这个单例对象的过程中需要确保线程安全,即在多线程环境下构造单例类的对象也是有且只有一个,这是单例模式较关键的一个地方。

示例代码

  1. 懒汉式,线程不安全
    延迟初始化,一般很多人称为懒汉法,写法一目了然,在需要使用的时候去调用getInstance()函数去获取Singleton的唯一静态对象,如果为空,就会去做一个额外的初始化操作。
public class Singleton {    private static Singleton instance = null;    private Singleton(){}    public static Singleton getInstance() {        if(instance == null)             instance = new Singleton();        return instance;    }}
  1. 懒汉式,线程安全
    需要做到线程安全,就需要确保任意时刻只能有且仅有一个线程能够执行new Singleton对象的操作,所以可以在getInstance()函数上加上 synchronized 关键字,类似于:
public static synchronized Singleton getInstance() {        if(singleton == null)             instance = new Singleton();        return instance;    }

对于绝大部分不需要同步的情况来说,synchronized 会让函数执行效率糟糕一百倍以上,所以就有了double-checked(双重检测)的方法:

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

当volatile用于一个作用域时,Java保证如下:
(适用于Java所有版本)读和写一个volatile变量有全局的排序。也就是说每个线程访问一个volatile作用域时会在继续执行之前读取它的当前值,而不是(可能)使用一个缓存的值。(但是并不保证经常读写volatile作用域时读和写的相对顺序,也就是说通常这并不是有用的线程构建)。
(适用于Java5及其之后的版本)volatile的读和写建立了一个happens-before关系,类似于申请和释放一个互斥锁[8]

我们假设两个线程A,B同时执行到了getInstance()这个方法,第一个if判断,两个线程同时为true,进入if语句,里面有个 synchronized 同步,所以之后有且仅有一个线程A会执行到 synchronized 语句内部,接着再次判断instance是否为空,为空就去new Singleton对象并且赋值给instance,A线程退出 synchronized 语句,交出同步锁,B线程进入 synchronized 语句内部,if判断instance是否为空,防止创建不同的instance对象,这也是第二个if判断的作用,B线程发现不为空,所以直接退出,所以最终A和B线程可以获取到同一个Singleton对象,之后的线程调用getInstance()函数,都会因为Instance不为空而直接返回,不会受到 synchronized 的性能影响。

  1. 饿汉式,线程安全
    “饿汉法”就是在使用该变量之前就将该变量进行初始化,这当然也就是线程安全的了
public class Singleton {private static Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){    return instance;}}

在该类进行加载的时候就会初始化该对象,而不管是否需要该对象。这么写的好处是编写简单,而且是线程安全的

0 0
原创粉丝点击