单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
特点:
1)单例类只能有一个实例
2)单例类的唯一实例必须由自己创建
3)单例类必须给所有其它对象提供自己唯一的实例
应用:系统要求一个类只有一个实例时使用。
单例模式的实现:
1)Singleton(饿汉模式):
public class Singleton {
private static final Singleton instance = new Singleton();
// 将构造函数私有化
private Singleton(){
}
// 静态工厂方法
public static Singleton getInstance(){
return instance;
}
public void show(){
System.out.println("Singleton");
}
}
说明:当这个类被加载时,静态变量instance会被初始化,此时类的私有构造器会被调用,这时,单例类的唯一实例就被创建出来了。
注意:虽然构造函数是私有的,但是利用反射及setAccessible(true)方法,仍然可以创建其它新的实例。
2)Singleton(懒汉模式)
public class Singleton{
private volatile static Singleton instance = null;
// 将构造函数私有化
private Singleton(){
}
// 静态工厂方法,返回此类的唯一实例
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
说明:与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。
注意:虽然构造函数是私有的,但是利用反射及setAccessible(true)方法,仍然可以创建其它新的实例。
优化:使用DCL双检查锁
public class Singleton{
// volatile保证:对instance变量的操作不会进行指令重排序。
private volatile static Singleton instance = null;
// 将构造函数私有化
private Singleton(){
throw new Exception("禁止通过反射调用私有的构造方法");
}
// 使用DCL(Double check lock)保证线程安全,并提高效率
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
使用volatile修饰变量:
1)线程A执行instance = new Singleton();这行代码可以分解为如下的3个步骤:
①类的加载、连接(验证->准备->解析)。
②初始化对象。 注:初始化后,类的加载就完成了。
③将instance指向刚分配的内存地址。 注:这一步和类的加载过程没有任何关系。
2)其中的②和③可能会被重排序:
分配对象的内存空间 --> 将instance指向刚分配的内存地址。(注意,此时对象还没有被初始化!) --> 初始化对象。
3)如果发生重排序,另一个并发执行的线程B就有可能在第一次检查时判断instance不为null,线程B接下来将访问instance所引用的对象,但此时这个对象可能还没有被A线程初始化!
3)使用静态内部类实现单例模式
public class Singleton{
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
说明:在第一次调用getInstance()方法时,即第一次加载静态内部类时才会去创建实例,故也实现了懒加载。
4)Enum实现单例模式
public class SingletonHolder {
public enum EnumSingleton {
SINGLETON_INSTANCE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static EnumSingleton getInstance() {
return EnumSingleton.SINGLETON_INSTANCE;
}
}
特点:
1)禁止利用反射对枚举进行实例化。
2)其特殊的序列化机制保证了无法通过反序列化得到实例的拷贝。
3)Enum的final克隆方法保证枚举永远无法被克隆。
jdk中的单例模式:
/**
* 饿汉式的单例模式,该类第一次被classloader加载的时候,实例就被创建出来了。
* Every Java application has a single instance of class
* <code>Runtime</code> that allows the application to interface with
* the environment in which the application is running.
*/
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
}