一天一个设计模式:单例模式
概念:
作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例。
特点:
1.单例类只能有一个实例
2.单例类必须创建自己的唯一实例
3.单例类必须给其他所有对象提供这一实例。
饿汉式单例类
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
/**
* 私有默认构造子
*/
private EagerSingleton(){}
/**
* 静态工厂方法
*/
public static EagerSingleton getInstance(){
return instance;
}
}
由于选用的是静态资源,在类加载的时候,静态变量instance就会被初始化,类的唯一变量也在这时候创建了出来。
饿汉式,顾名思义,急于创建对象,在类加载的时候就完成了对象的创建。
饿汉式是一种采用“空间换取时间”,当类装载的时候就会创建类的实例,不管你用不用,先创建,调用的时候,无需判断直接使用,节约了时间。
懒汉式单例
public class LazySingleton {
private static LazySingleton instance = null;
/**
* 私有默认构造子
*/
private LazySingleton(){}
/**
* 静态工厂方法
*/
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
只有在真正需要的时候才会去创建,采用的是时间换空间,由于是线程安全的,会降低访问速度。固可使用双检锁的方式进行优化。
双重检查加锁
机制:并不是每次进入getInstance方法都需要同步,而不是先同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一检查,进入同步块后,在检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查,这样一来就只需要同步一次,减少了多次在同步情况下进行判断所浪费的时间。
双检锁的关键是,使用volatile,它在此处的作用是,该变量将不会被某一个线程缓存,而是在共享内存中,被所有进行读写的内存共享到,从而保证多个线程能够正确处理该变量。
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关键字会屏蔽虚拟机的一些代码优化,所以运行效率并不高,所有其实应该尽量避免使用双检锁的方式来实现单例。
懒加载模式(内部类形式)
什么是内部类:
内部类就是指在类里面的类,被static修饰的内部类,称为类级内部类,无static修饰的内部类称为对象级内部类。
类级内部类是外部类的static部分,它的对象与外部类对象间并没有依赖关系,因此可以直接创建。而对象级内部类的实例,是绑定在外部对象的实例中的。
类级内部类中,可以定义静态方法,在静态方法中只能够引用外部类中的静态成员或者成员变量。
类级内部类相当于外部类的成员,只有在第一次被使用的时候才会加载。
多线程缺省同步锁的相关知识:
在下面操作下,jvm会自动为该操作进行同步,以避免出现并发安全问题。
1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据。
2.访问final字段时,
3.在创建线程前创建对象时,
4.线程可以看见它将要处理的对象时。
综上的解决思路:
采用静态初始化器的方式通过jvm来保证线程的安全性,采用类级内部类的方式实现延迟加载。
public class Singleton {
private Singleton(){}
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
在getInstance第一次被调用时,会读取内部类的instance,这时,类级内部类完成初始化,从而创建单例,实现了延时加载,由于是静态成员,只有在类加载的时候才加载一次,且通过jvm的缺省方式实现了线程安全问题。
单例类枚举:
单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。
public enum Singleton {
/**
* 定义一个枚举的元素,它就代表了Singleton的一个实例。
*/
uniqueInstance;
/**
* 单例可以有自己的操作
*/
public void singletonOperation(){
//功能处理
}
}
使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。