单例模式(校招经典面试题)!!!!

前置知识:

一、volatile解决内存可见性

首先明确什么是内存可见性:

假如有两个线程t1和t2,t1频繁读取主内存,效率比较低,就被优化成直接读自己的工作内存;t2修改了主内存的结果,由于t1没有读主内存,导致修改不能被识别到

volatile就告诉计算机你不要优化,会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。

正题:

一、单例模式是实际开发中比较常用的一种模式,实现方法也五花八门,在这我主要介绍2种比较经典的模式

1、饿汉模式(急迫)

2、懒汉模式(从容)

举个简单的例子:假如老师布置了十门作业

饿汉模式就是把所有的作业都写完,即使很多;而懒汉模式就是如果明天只检查三门我就只写三门作业

在实际开发中当我们某个资源有限的情况,把所有的任务都完成是没有必要的,只需完成最近需要的就可

(1)下面首先先完成饿汉单例模式的创建:

class Singleton {
    // 唯一实例的本体
    private static Singleton instance = new Singleton();

    // 获取到实例的方法
    public static Singleton getInstance() {
        return instance;
    }

    // 禁止外部 new 实例.
    private Singleton() { }
}

public class ThreadDemo17 {
    public static void main(String[] args) {
        // 此时 s1 和 s2 是同一个对象!!
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();

       
    }
}
private static Singleton instance = new Singleton();

被static修饰,该属性是类属性,在JVM中,每个类对象只有一份,类对象这个成员也只有一份。但要保证是唯一实例本体,就必须禁止new外部对象。

private Singleton() { }

这样就保证了在内部实例好对象的同时紧张外部new新的对象,保证了唯一实例本体,也就是单例。

(2)懒汉模式实现单例

核心思想:非必要,不创建(多的活一份不干)

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

public class Demo8 {
    public static void main(String[] args) {
     SingletonLazy s1=SingletonLazy.getInstance();
     SingletonLazy s2=SingletonLazy.getInstance();
        System.out.println(s1==s2);
    }
}

!!!!!多个线程下调用getInstance,是否会出现安全问题???

饿汉模式,也就是(1),认为线程是安全的,只是读数据,而没有修改数据;

而(2)修改了数据,可能会导致多个线程修改一个变量,进而导致线程不安全问题,下面链接的第二条

线程不安全的原因(实际开发中经常出bug的地方)-CSDN博客

那么如何解决呢,利用synchronized(加锁)来解决,也就是上述代码,那么这个代码真的很好嘛,网上百分之九十以上的博客是这么教你的!!!但是还有一个小小的问题,即使发生的概率很小,但如果在实际开发中可能就会造成bug!!!!!

instance=new SingletonLazy();

这一步就会可能导致指令重排序!!!

解决方法也很简单,利用volatile

private static SingletonLazy instance=null;
//加上volatile
volatile private static SingletonLazy instance=null;

所以,最终代码如下:!!!!

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

public class Demo8 {
    public static void main(String[] args) {
     SingletonLazy s1=SingletonLazy.getInstance();
     SingletonLazy s2=SingletonLazy.getInstance();
        System.out.println(s1==s2);
    }
}

小结:

单例模式,线程安全问题

饿汉模式:天然就是安全的,只是读操作

懒汉模式:不安全 有读也有写

1、加锁(synchronized),把if和esle变成原子操作

2、双重if,减少不必要的加锁操作

3、使用volatile禁止指令重排序,保证后续线程拿到的是完整对象

你可能感兴趣的:(单例模式,java,bug,面试)