一、Java中的单例:
特点:
① 单例类只有一个实例
② 单例类必须自己创建自己唯一实例
③ 单例类必须给所有其他对象提供这一实例
二、两种模式:
①懒汉式单例<线程不安全>
在类加载时,不创建实例,运行调用时创建。类加载快,在运行时获取对象速度慢
示例:
public class Singleton { private static Singleton uniqueInstance = null; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; }
②饿汉式单例<线程安全>
在类加载的时候,就完成初始化。所以类加载慢,但是在运行时获取对象快
示例:
//饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 { //私有的默认构造子 private Singleton1() {} //已经自行实例化 private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() { return single; } }
三、如何保证多线程下的单例:
① 同步锁<主要是通过使用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; } }