MST

星途 面试题库

面试题:Java中常见的反模式之单例模式误用

在Java中,单例模式是一种常用设计模式,但也容易出现反模式。请描述一种单例模式在多线程环境下可能出现的反模式,并说明如何避免这种反模式。
16.9万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

反模式描述

在多线程环境下,使用懒汉式单例模式且未进行适当同步控制时,可能出现反模式。例如以下代码:

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

当多个线程同时调用 getInstance() 方法,且 instancenull 时,可能会创建多个实例,破坏单例模式的唯一性。

避免方法

  1. 同步方法:在 getInstance() 方法上添加 synchronized 关键字。
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

这种方式虽然能保证线程安全,但每次调用 getInstance() 方法都要获取锁,性能较低。

  1. 双重检查锁定(DCL):结合 synchronized 和双重检查机制。
public class DoubleCheckedLockingSingleton {
    private volatile static DoubleCheckedLockingSingleton instance;

    private DoubleCheckedLockingSingleton() {}

    public static DoubleCheckedLockingSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }
}

这里使用 volatile 关键字保证 instance 的可见性,避免指令重排导致其他线程获取到未完全初始化的实例。第一次 if (instance == null) 检查避免不必要的同步,提高性能。

  1. 静态内部类:利用类加载机制实现线程安全的懒加载。
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}

    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }

    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

类加载时,SingletonHolder 不会立即加载,只有在调用 getInstance() 方法时才会加载并初始化 INSTANCE,利用了类加载的线程安全性。

  1. 枚举单例:简单且线程安全。
public enum EnumSingleton {
    INSTANCE;
}

枚举类型在Java中天然线程安全,并且在序列化和反序列化时也能保证单例。