这种
1.单例模式简介
单例模式属于设计模式中的创建型模式。它主要用于一个类只能有一个实例的场景(想想一些只能有一个或者只需要一个的资源的场景),哪怕是多线程在访问这个实例,除此之外,需要提供一个全局访问它的点。
2.单例模式实现的方式
单例模式有很多实现方法,在这里只提供几种常见的,分别是饿汉、懒汉、静态内部类、枚举类
2.1 饿汉
public class HungrySingleton {
// 饿汉模式,程序启动的时候直接加载 线程安全 线程安全由虚拟机保证
// 这里使用final关键词是为了防止子类继承破坏单例
private static final HungrySingleton instance = new HungrySingleton();
// 构造方法设为私有,防止外部new创建
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return instance;
}
}
优点:线程安全由虚拟机保证,可靠,外部需要使⽤的时候获取即可。
缺点:
(1)无论程序中是否使用都会在程序启动之初进⾏创建,有可能浪费空间(创建了但不使用)。
(2)这种形式的单例过多可能导致程序启动时间过长。
(3)私有化构造器方法不能防止外部创建会被暴力发射破坏。
2.2 懒汉
顾名思义,只有在真正使用的时候才会加载并创建该单例对象。
对于这种实现方式的单例模式,主要有两种,分别为
public class LazySingleton {
// 这里使用volatile是为了防止instance = new LazySingleton();发生指令重排
private static volatile LazySingleton instance;
private LazySingleton() {};
public static synchronized LazySingleton LazySingleton() {
if (null != instance) {
return instance;
}
instance = new LazySingleton();
return instance;
}
}
此方法实现主要通过方法锁的形式实现多线程安全,但这种加锁方式过于笨重,多线程在该方法上都是串行化,对于并发量高的常见不建议使用,以下是一种优化后的方案:
public class LazySingleton {
// 这里使用volatile是为了防止instance = new LazySingleton();发生指令重排
private static volatile LazySingleton instance;
public static LazySingleton getInstance() {
if (null != instance) {
return instance;
}
synchronized (LazySingleton.class) {
if (null == instance) {
instance = new LazySingleton();
}
}
return instance;
}
}
此方法实现也叫双重校验锁形式的单例,相较于把锁加载方法上,第一重校验单例是否为空就能处理大部分的并发请求,减小了锁的竞争。一定程度上优化了性能。
两个问题:
(1)为什么要双重校验:
有了第一重校验,还要有第二重校验的原因,可以考虑这样一个场景,即两个线程并发访问getInstance方法,且此时还没有创建instance对象,那么两个线程均能通过第一重校验锁,那么到了同步代码块,假设第一个线程先进去创建了单例对象(第二个线程阻塞等待),释放锁,第二个线程进去又一次创建了单例对象(没有第二重校验的话),破坏了单例的特性。
(2)volatile关键词的作用
这里使用volatile关键词的作用主要是为了防止instance = new LazySingleton();语句发生指令重排。这个语句如果发生指令重排,有可能先给instance引用赋值,再调用LazySingleton的构造方法,如果这两个操作之间有别的线程调用getInstance方法,则会返回一个还没有初始化完的LazySingleton对象。
优点:线程安全,懒加载,一定程度上可以节省内存资源。
缺点:加锁的⽅式实现线程安全耗费性能。私有构造器方法防止外部创建会被暴力发射破坏
2.3 枚举
public enum EnumSingleton {
INSTACE;
}
使用
public class Test {
public static void main(String[] args) {
System.out.println(EnumSingleton.INSTACE.toString());
}
}
优点:线程安全,其线程安全由虚拟机保证,而且可以防止暴力反射破坏单例,总得来说也是一种比较推荐的方式
缺点:饿汉式实现思想。有可能浪费空间,但设计合理可以避免这个问题。
2.4 静态内部类
public class InnerSingleton {
private InnerSingleton() {}
private static class InnerClass {
private static InnerSingleton instance = new InnerSingleton();
}
public static InnerSingleton getInstance() {
return InnerClass.instance;
}
}
优点:不用加锁实现两线程安全,其线程安全由虚拟机保证,且也实现了懒加载的思想,用的时候再创建,这是一种比较推荐的单例模式实现方式。
缺点:私有化构造器方法不能防止外部创建会被暴力发射破坏。