单例模式(Singleton)
如果某个对象只需要在应用的生命周期内保持只有一个对象,那么就可以使用单例模式
单例模式的关键点
- 无法通过new来随意创建对象,构造函数为private
- 提供获取唯一实例对象的方法,通常是getInstance()
- 多线程并发的情况下保证安全,不会new出多个实例
- 尽量避免单例对象被使用者通过反射创建
- 避免通过反序列化创建单例对象
1. 饿汉式
优点:实现简单,调用时对象已经存在,无需等候对象创建
缺点:对象不一定会被使用到,因此可以认为浪费空间,无法避免反序列化与反射
/**
* 饿汉式
* 不管三七二十一,加载一个再说
* 问题:有可能这个类无需使用,浪费内存空间
*
* @author xiaoyingge
* @description
* @date 2020/4/18 18:21
*/
public class Singleton1 {
/**
* static final 会在类加载过程中实例化,因此只会执行一次Singleton1
*/
private static final Singleton1 INSTANCE = new Singleton1();
/**
* 禁止使用new
*/
private Singleton1 () {
}
public static Singleton1 getInstance () {
return INSTANCE;
}
public static void main (String[] args) {
Singleton1 singleton1 = Singleton1.getInstance();
Singleton1 singleton2 = Singleton1.getInstance();
System.out.println(singleton1 == singleton2);
}
}
2. 懒汉式(双重检查)
很多源码处可以见到,运用双重检查实现饿汉式单例
优点:使用时加载,不浪费空间
缺点:有一定程度的锁竞争,无法避免反序列化与反射
/**
* 双重检查,能保证线程安全,很多源码可以看到运用
*
* @author xiaoyingge
* @description
* @date 2020/4/18 18:21
*/
public class Singleton6 {
/**
* static final 会在类加载过程中实例化,因此只会执行一次Singleton1
*/
private static volatile Singleton6 INSTANCE;
/**
* 禁止使用new
*/
private Singleton6 () {
}
public static Singleton6 getInstance () {
if (INSTANCE == null) {
//加 sleep 只是为了让问题容易暴露出来
synchronized (Singleton6.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton6();
}
}
}
return INSTANCE;
}
}
3.懒汉式(静态内部类)
由JVM的机制保证多线程下安全
优点:使用时加载,不浪费空间
缺点:无法避免反序列化与反射
/**
* 静态内部类
* JVM加载时不会加载内部类,因此可以实现懒汉式
*
* @author xiaoyingge
* @description
* @date 2020/4/18 18:21
*/
public class Singleton7 {
/**
* 禁止使用new
*/
private Singleton7 () {
}
public static Singleton7 getInstance () {
return Singleton7Holder.INSTANCE;
}
private static class Singleton7Holder {
private static final Singleton7 INSTANCE = new Singleton7();
}
}
4.enum单例
理解上有些困难,但是确实是单例的最佳实践
本质上是abstract类,因此没有构造函数,无法new
反序列化出来的对象还是同一个对象
/**
* 枚举本质上是个final类,定义的枚举值实际上就是一个枚举类的不可变对象
* 在Enum类加载的时候,就已经实例化了这个对象,且枚举对象不可以new
*
* @author xiaoyingge
* @description
* @date 2020/4/18 18:21
*/
public enum Singleton8 {
INSTANCE;
public static Singleton8 getInstance () {
return INSTANCE;
}
public String doMyWork () {
return "this is my job";
}
public static void main (String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(Singleton8.INSTANCE.doMyWork())).start();
}
}
}