单例模式的一道面试题

Java中单例模式是最简单的设计模式之一,分懒汉式和饿汉式

两者本质上的区别就是在什么时候实例化类的 单例对象。
饿汉式在类加载时就初始化,容易浪费内存,也没有达到lazy loading的效果
懒汉式只有在需要的才初始化,但是线程不安全,无法在多线程下使用
单例模式的一道面试题_第1张图片
针对该面试题,我们先看懒汉式和饿汉式的设计代码

饿汉式

public class Factory {
    /**
      *是否 Lazy 初始化:否
      *是否多线程安全:是
      *实现难度:易
      *描述:这种方式比较常用,但容易产生垃圾对象。
      *优点:没有加锁,执行效率会提高。
      *缺点:类加载时就初始化,浪费内存。
      *它基于 classloder 机制避免了多线程的同步问题,
      * 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
     * 在单例模式中大多数都是调用 getInstance 方法,
      * 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
      * 这时候初始化 instance 显然没有达到 lazy loading 的效果。
      */
    private static Factory instance = new Factory();

    private Factory() {
        System.out.println("**********" + Thread.currentThread().getName() + "-初始化" + Factory.class.getSimpleName() + "***************");
    }

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

    public void print() {
        System.out.println("这是工厂的功能方法");
    }
}

/**
 * @author admin
 */
public class SomeTest {
    public static void main(String[] args) {
        for (int i=0;i<3;i++){
            new Thread(()->{
                Factory factory=Factory.getInstance();
                factory.print();
            },"线程"+i).start();
        }

    }
}

单例模式的一道面试题_第2张图片
一开始就初始化了instance实例,所以不存在线程安全问题

饿汉式

public class Factory {

    /**
      *是否 Lazy 初始化:是
      *是否多线程安全:否
      *实现难度:易
      *描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
      *这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
     */
    private static volatile Factory instance = null ;

    private Factory() {
        System.out.println("**********" + Thread.currentThread().getName() + "-初始化" + Factory.class.getSimpleName() + "***************");
    }

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

    public void print() {
        System.out.println("这是工厂的功能方法");
    }
}

这块就是实现线程安全的代码
单例模式的一道面试题_第3张图片
单例模式的一道面试题_第4张图片
为什么有两个判空呢,而不直接在方法上面加synchronized呢

原因:
如果直接对整个方法加synchronized 会导致每个线程都会进入这个同步的if判断,如果此时有很多线程,会导致程序效率大大降低。
通过synchronized (Factory.class)同步代码块,仅对判断为空时,下面的new Factory()做同步处理,这样可以避免上述问题,但是这时候如果不在里面再判一次空的话,第一次在创建时可能会有几个线程同时进入(没创建是instance为null),所以同步块内部还需要一次判空并且把instance变量设置为volatile .

在多线程环境下,volatile是对其修饰的变量直接进行内存操作,而不创建副本

你可能感兴趣的:(Java)