单例模式最佳实践
简单介绍
单例模式是最简单的设计模式之一,提供了一种创建对象的方式,确保在整个系统中只有一个对象被创建.单例模式解决了频繁创建重复对象的问题节约资源,可以省略创建对象所需要花费的时间,对于一些重量级对象而言这点是很重要的.并且因为不需要频繁创建对象 GC 的压力也会有所减轻.
单例模式的一些实现方式
通常来说在 Java 中的单例模式分为饿汉式和懒汉式,而且单例类需要一个 private 的构造函数防止被其他代码实例化.下面来具体说一下java 中单例模式的实现.
饿汉式
public class Singleton{
private static Singleton instance =new Singleton();
//私有化构造方法
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
饿汉式的单例模式代码简单,线程安全,通过classLoader机制保证了单例对象的唯一性 但是不能确保instance 是在调用getInstance()方法的时候生成的不能达到懒加载效果
懒汉式
public class Singleton{
private static Singleton instance;
private Singleton(){}
//加入 synchronize 保证线程安全
public synchronized static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
在获取的时候创建实例可以达到延迟加载的效果并且加入了 synchronize 保证线程安全,但每次调用代码的时候都要加锁,性能比较低还有可能发生阻塞
DCL双重校验锁
public class Singleton{
//volatile防止指令重排序
private static volatile Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
//加入第二次校验
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
双重校验锁就是为了解决上述问题而存在的,先检查实例是否存在然后再去创建,可以不用每次调用方法都获取同步锁性能会有一些提升,减小的锁的颗粒度.但是 java 对象的创建和赋值不是一步操作的,有可能先去赋值给中instance之后才去创建 Singleton 这时添加volatile关键字防止指令重排序解决了这个问题.
静态内部类
public class Singleton{
private Singleton(){}
public static class SingleHoler{
public static final Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingleHoler.instance;
}
}
使用静态内部类时,当Singleton被创建的时候不会去加载SingleHoler,只有第一次调用getInstance()方法时才回去创建instance,加载SingleHoler将常量池中的符号引用替换成直接引用,这种方式不仅保证了线程安全而且可以达到延迟加载的效果.
最佳实践
public enum Singleton{
INSTANCE;
public void print(){
System.out.println("快乐就完事了!");
}
}
这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。 --《Effective Java 中文版 第二版》
简单到不能再简单了啊.jvm 在加载枚举类的时候会使用loadClass方法使用同步代码块解决线程安全问题.使用 enum 的单例模式还能避免反序列化破坏单例并且不能被反射攻击.