双汉模式
相信一听到单例模式,大家第一印象想起的就是当年学的”两个汉子“了 – 饿汉式 + 懒汉式
饿汉式
public class MySingle {
private static final MySingle INSTANCE = new MySingle();
private MySingle(){};
private static MySingle getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
MySingle mySingle = MySingle.getInstance();
MySingle mySingle2= MySingle.getInstance();
System.out.println(mySingle == mySingle2);
}
}
构造方法权限私有是为了不让直接创建,常用在单例类与工具类
显然输出的结果是 true
类加载到内存后,就实例化一个单例,JVM保证线程安全
这种方式实现单例比较简单实用
唯一的缺点是:不管是否用到,类加载时就完成实例化
但还是优先推荐使用
懒汉式
一般懒汉式为了线程安全起见都需要采用双重检查
public class YourSingle {
private static volatile YourSingle INSTANCE;
private YourSingle(){};
public static YourSingle getInstance(){
if(INSTANCE == null){
// 双重检查
synchronized (YourSingle.class){
if(INSTANCE == null){
INSTANCE = new YourSingle();
}
}
}
return INSTANCE;
}
}
这里需要添加volatile 是因为防止java的指令重排问题重排
这种方法看起来比较完美,推迟了对象的创建,看起来也很显高端
其实没必要,而且大可不必,因为如果是对于频繁的多线程来说,引入判断与锁对于性能来说就已经有一定的弱化
推荐的单例
匿名内部类
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private final static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
使用内部类的可以推迟对象的创建,还可以保证线程安全,因为JVM加载类的时候只加载一次,这个在真实开发的过程中是比较常用也满分推荐使用的
枚举类
《Effective Java》里面推荐了一种最完美的方式 – 枚举类
public enum EnumSingle {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 100; i++){
new Thread(()->{
System.out.println(EnumSingle.INSTANCE.hashCode());
}).start();
}
}
}
此方式看起来确实怪了一点,但是却可以保证前面所有优点的同时还可以防止被反序列化
总结
以上也很容易看出来,单例模式咱们就不要再当“两汉”啦,综合比较推荐匿名内部类的方式,虽然枚举类最完美,但是真的有时在代码可读性差了点