Java面试-通过单例模式的8种写法搞定单例模式面试

单例模式的8种写法

为什么需要单例模式
  • 节省内存和计算
  • 保证结果正确
  • 方便管理
单例模式适用场景
  • 无状态的工具类:如日志工具类
  • 全局信息类:如网站访问次数记录类
饿汉式(静态常量)(可用)
/**
 * 〈饿汉式(静态常量)〉
 *  可用
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton1 {

    private final static Singleton1 INSTANCE =
            new Singleton1();
    private Singleton1(){

    }

    public static Singleton1 getInstance(){
        return INSTANCE;
    }

}
饿汉式(静态代码块)(可用)
/**
 * 〈饿汉式(静态代码块)〉
 * 可用
 *
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton2 {

    private final static Singleton2 INSTANCE;

    static {
        INSTANCE = new Singleton2();
    }

    private Singleton2() {

    }

    public static Singleton2 getInstance() {
        return INSTANCE;
    }

}
懒汉式(线程不安全)(不可用)
/**
 * 〈懒汉式(线程不安全)〉
 * 不可用
 *
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton3 {

    private static Singleton3 instance;
    
    private Singleton3() {

    }

    public static Singleton3 getInstance() {
        //多线程下可能多次创建实例,就不是单例了
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }

}
懒汉式(线程安全)(不推荐)
/**
 * 〈懒汉式(线程安全)〉
 *  效率低不推荐使用
 *
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton4 {

    private static Singleton4 instance;

    private Singleton4() {

    }
    
    //加上synchronized  安全,但是效率低
    public synchronized static Singleton4 getInstance() {

        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }

}
懒汉式(加锁,线程不安全)(不可用)
/**
 * 〈懒汉式(线程不安全)〉
 * 依然不可用
 *
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton5 {

    private static Singleton5 instance;

    private Singleton5() {

    }

    public static Singleton5 getInstance() {

        if (instance == null) {
            synchronized (Singleton5.class) {
                instance = new Singleton5();
            }
        }
        return instance;
    }

}
双重检查(推荐面试使用)(可用)
/**
 *	懒汉式
 * 〈双重检查(推荐面试使用)〉
 * 可用
 *
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton6 {

    private volatile static Singleton6 instance;

    private Singleton6() {

    }

    public static Singleton6 getInstance() {

        if (instance == null) {
            /*
                线程a,b都到了这个位置
                a先进入获得锁
                初始化instance
                a释放锁b获得锁进入
                发现instance已经初始化了
                跳过初始化代码
             */
            synchronized (Singleton6.class) {
                if (instance == null) {
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }

}

双重检查的优点:

  • 线程安全
  • 延迟加载,效率较高

为什么要使用双重检查,单次检查不行吗

  • 双重检查能保证线程安全
  • 如果只是单次检查,第一个检查后可能多个线程都进入等待锁,锁释放之后会重复初始化

为什么要用volatile?

  • 新建对象实际上有3个步骤,并不是原子性的
    • 创建一个空对象
    • 调用构造方法
    • 创建好的实例赋值给引用
  • 重排序会带来NPE
    • 步骤二和步骤三发生重排序,执行到步骤三时实例就不为空了,如果有新线程进来发现不为空就拿去用了,此时的实例是未调用构造方法的不完整的
  • 防止重排序
  • 保证可见性(初始化完了之后其他线程能马上看到)
静态内部类(推荐用)(可用)
/**
 * 〈懒汉式(静态内部类)〉
 * 推荐使用
 *
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public class Singleton7 {


    private Singleton7() {

    }

    private static class SingletonInstance {
        private static final Singleton7 INSTANCE
                = new Singleton7();
    }

    public static Singleton7 getInstance() {
        return SingletonInstance.INSTANCE;
    }

}
枚举(推荐用)(可用)(生产中最佳写法)
/**
 * 〈枚举〉
 * 推荐使用
 * 
 *   调用:Singleton8.INSTANCE.whatever();
 *   
 * @author Chkl
 * @create 2020/3/4
 * @since 1.0.0
 */
public enum Singleton8 {
    INSTANCE;

    //其中的方法
    public void whatever(){
        
    }


}

哪种单例的实现方案最好啊?

  • 枚举最好!
    • 《Effective Java》中明确表示枚举是最佳的
    • 写法简单
    • 线程安全
    • 符合懒加载机制
    • 避免反序列化破坏单例

饿汉式的缺点

  • 资源效率不高

懒汉式的缺点

  • 写法复杂
  • 容易写成线程不安全
发布了25 篇原创文章 · 获赞 25 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41170102/article/details/104649116