请注意,本文编写于 573 天前,最后修改于 494 天前,其中某些信息可能已经过时。
设计模式那么多,面试中常问的也就单例模式了
考察:单例模式的五种实现方式,jdk 中使用单例模式的场景,为何 DCL(双重检查锁) 实现时要使用 volatile
修饰静态变量
1、饿汉式
顾名思义,很饿,所以类初始化时就创建了对象(单例的,用 static 修饰)
public class Singleton1 implements Serializable {
private static final Singleton1 INSTANCE = new Singleton1();
private Singleton1(){
//构造私有
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
但是上面的代码不够健壮,可以通过反射或者反序列化来破坏单例,我们进一步修改
public class Singleton1 implements Serializable {
private static final Singleton1 INSTANCE = new Singleton1();
private Singleton1(){
//防止反射破坏单例
if (INSTANCE != null){
throw new RuntimeException("单例对象无法重复创建实例");
}
System.out.println("构造私有");
}
public static Singleton1 getInstance() {
return INSTANCE;
}
//防止反序列化破坏单例,方法名固定的
public Object readResolve(){
return INSTANCE;
}
public void method(){
System.out.println("method");
}
public static void staticMethod(){
System.out.println("static method");
}
}
测试代码
public class TestSingleton {
public static void main(String[] args) throws Exception {
Singleton1.staticMethod();
Singleton1.getInstance().method();
System.out.println(Singleton1.getInstance());
//反射调用私有构造破坏单例
//reflection(Singleton1.class);
//反序列化破坏单例
//serializable(Singleton1.getInstance());
//Unsafe破坏单例,无法预防
unsafe(Singleton1.class);
}
public static void reflection(Class<?> clazz) throws Exception {
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
System.out.println("反射创建实例," + constructor.newInstance());
}
public static void serializable(Object instance) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
System.out.println("反序列化创建实例," + ois.readObject());
}
private static void unsafe(Class<?> clazz) throws Exception {
Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
System.out.println("Unsafe创建实例," + o);
}
}
2、枚举饿汉式
枚举饿汉式能天然防止反射、反序列化破坏单例
public enum Singleton2 {
INSTANCE;
private Singleton2() {
System.out.println("private Singleton2()");
}
@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public static Singleton2 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
3、懒汉式
public class Singleton3 implements Serializable {
private static Singleton3 INSTANCE = null;
private Singleton3() {
System.out.println("private Singleton3()");
}
public static synchronized Singleton3 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton3();
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
其实只有首次创建单例对象时才需要同步锁,但该代码实际上每次调用都会同步锁,因此有了后面的双检锁改进
4、双检锁懒汉式
public class Singleton4 implements Serializable {
private Singleton4() {
System.out.println("private Singleton4()");
}
// 加 volatile 解决共享变量的可见性,有序性问题
private static volatile Singleton4 INSTANCE = null;
public static Singleton4 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton4.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
INSTANCE = new Singleton4()
过程:创建对象、调用构造、给静态变量赋值,后两步可能被指令重排序优化,变成先赋值、再调用构造,此时如果线程1 先执行了赋值,线程2 执行到第一个 INSTANCE == null
时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象
加上 volatile
可以解决这一问题
5、内部类懒汉式
public class Singleton5 implements Serializable {
private Singleton5() {
System.out.println("private Singleton5()");
}
private static class Holder {
static Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance() {
return Holder.INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
JDK 中单例的体现
- Runtime 体现了饿汉式单例
- Console 体现了双检锁懒汉式单例
- Collections 中的 EmptyNavigableSet 内部类懒汉式单例
- ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
- Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例
版权属于:乐心湖's Blog
本文链接:https://www.xn2001.com/archives/668.html
声明:博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!