Java单例模式
确保每个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式避免了状态不一致的情况。
特点:
·单例类只有一个实例;
·单例类自己创建那个唯一的实例;
·单例类为整个系统的其他对象提供这一实例。
单例模式保证了全局对象的唯一性。例如配置类等。
单例的四大原则:
·构造私有;
·以静态方法或者枚举方法返回实例;
·确保只有一个实例,尤其是多线程环境;
·确保反序列化不会构建对象。
实现实例化的方法有:
饿汉式(立即加载)

public class Singleton {
private Singleton() {
System.out.println("构造函数Singleton");
}
private static Singleton single = new Singleton();
public static Singleton getInstance() {
System.out.println("getInstance");
return single;
}
}

懒汉式(延迟加载)

public class Singleton {
private Singleton() {
System.out.println("构造函数Singleton");
}
private static Singleton single=null;
public static Singleton getInstance() {
System.out.println("getInstance");
if(single==null){
single = new Singleton();
}
return single;
}
}

同步锁(解决多线程问题)

public class Singleton {
private Singleton() {
System.out.println("构造函数Singleton");
}
private static Singleton single=null;
public synchronized static Singleton getInstance() {
System.out.println("getInstance");
if(single==null){
single = new Singleton();
}
return single;
}
}

双重检查锁(解决多线程下使用同步锁的性能问题)
为了禁止指令重排序,保证对象在“引用被赋值”之前“已经完全初始化”,实例变量要使用volatile修饰。
在JVM中,执行 single = new Singleton() 这行代码,在字节码层面并不是原子操作,而是分为三个步骤:
-
分配内存:在堆中开辟一块内存空间(此时内存中全是默认值,比如
int=0,引用=null)。 -
初始化对象:调用构造器,将内存中的值填充为真正的初始值(比如
int=5,引用指向具体对象)。 -
建立关联(赋值):将堆内存的地址赋值给栈中的 single 引用变量。
不使用volatile修饰可能会导致步骤2未执行,步骤3已经执行,其他线程会判断single 不为null而直接使用,但是single并未初始化而导致空指针异常。

public class Singleton {
private Singleton() {
System.out.println("构造函数Singleton");
}
private volatile static Singleton single=null;
public static Singleton getInstance() {
System.out.println("getInstance");
if(single==null){
synchronized (Singleton.class) {
if(single==null) {
single = new Singleton();
}
}
}
return single;
}
}

内部静态类(懒汉式,延迟加载,线程安全,但是不能解决反序列化下重新构建实例的问题)
原理:延迟加载和JVM类加载锁(线程安全)
唯一的实例化时机:Java类加载的“初始化阶段”是由JVM底层加锁进行的。当外部类Singleton 被加载时,内部静态类InnerObject 不会被加载。只有第一次调用getInstance()方法时才会加载和初始化内部静态类。
线程安全:JVM在类初始化时,会获得初始化锁,因此多个线程同时调用getInstance()方法,内部静态类也只会初始化一次。
虽然构造器是private的,但是可以通过反射强行调用构造器,因此使用反射仍可以创建第二个实例。

public class Singleton {
private Singleton() {
System.out.println("构造函数Singleton");
}
private static class InnerObject {
private static final Singleton single =new Singleton();
}
public static Singleton getInstance() {
return InnerObject.single;
}
}

内部枚举类实现(饿汉式,防止反射和序列化攻击)
枚举类enum会继承java.lang.Enum 的 final 类。枚举常量会被编译成静态成员(public static final修饰),在静态代码块中实例化,同样会获得类加载锁,保证了实例的唯一性。
Java反射会有检验,通过反射创建枚举实例时会抛出异常IllegalArgumentException。
枚举的序列化只写入常量的name,反序列化会从缓存哈希表中获取已经存在的单例,不会重新构建。
枚举类已经继承Enum,无法继承其他类。

public class SingletonFactory {
private enum EnumSingleton {
Singleton;
private Singleton singleton;
private EnumSingleton() {
singleton = new Singleton();
}
public Singleton getInstance() {
return singleton;
}
}
public static Singleton getInstance(){
return EnumSingleton.Singleton.getInstance();
}
}
public class Singleton {
private Singleton() {
System.out.println("构造函数Singleton");
}
}更多推荐

所有评论(0)