单例
一、Java中的单例:
特点:
1. 单例类只有一个实例
2. 单例类必须自己创建自己唯一实例
3. 单例类必须给所有其他对象提供这一实例
二、两种模式:
1.懒汉式单例<线程不安全>
在类加载时,不创建实例,运行调用时创建。类加载快,在运行时获取对象速度慢
示例:
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
2.饿汉式单例<线程安全>
在类加载的时候,就完成初始化。所以类加载慢,但是在运行时获取对象快
示例:
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
//私有的默认构造子
private Singleton1() {}
//已经自行实例化
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
三、如何保证多线程下的单例:
1.同步锁<主要是通过使用synchronized来加互斥锁进行同步控制。>
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,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
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;
}
}