volatile关键字的作用 以及 单例模式(饿汉模式与懒汉模式的区别及改进)-????懒汉模式多线程版

时间:2024-03-05 09:42:50

在线程安全方面,上面的饿汉模式是在多线程下是安全的,而懒汉模式在多线程下是不安全的;

因为,如果多个线程同时访问一个变量,那么不会出现不安全问题,如果多个线程同时修改一个变量,就有可能出现不安全问题;

饿汉模式下,只进行了访问,没有涉及到修改

在这里插入图片描述

懒汉模式下,不仅进行了访问,还涉及了修改,那么下面就讲解以下懒汉模式在多线程下如何会产生不安全

在这里插入图片描述

在这里插入图片描述

既然出现了不安全问题,那么如何将懒汉模式修改成安全的呢?

????方法:进行加锁,使线程安全

在这里插入图片描述

但是,如果锁加在这个地方,仍然是不安全的,因为,这样还是会进行穿插执行,如果两个并发的进入的 if 语句中,那么,就会进行锁竞争,假设,thread1 获取到了锁,thread2 在阻塞等待,等到 thread1 创建一次对象,释放锁后,thread2 就又会载获取到锁,进行创建对象,所以,这个加锁操作并没有保证它是一个整体(非原子性)

在这里插入图片描述

所以说,并不是加了锁就安全,只有锁加对了才会安全,在加锁的时候要保证以下几方面:

  1. 锁的 {} 的范围是合理的,能够把需要作为整体的每个部分都包括进去;

  2. 锁的对象能够起到锁竞争的效果;

懒汉模式多线程版改进????

将if语句和new都放在锁里面成为一个整体,这样就避免了会穿插执行;

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

但是上述代码还有一个问题,每当调用getInstance时,都会尝试去进行加锁,而加锁是一个开销很大的操作,而懒汉模式之所以会出现线程不安全问题,是因为只是在第一次调用getInstance方法new对象时,可能会出现问题,但是,只要new完对象以后,就不用再进行锁竞争了,直接访问就可以了,所以再次进行优化????:

    public static SingleTon getInstance() {
        //在最外面在进行一次判断
        if(instance == null) {
            synchronized (SingleTon.class) {

                if(instance == null) {
                    instance = new SingleTon();
                }

            }
        }
        return instance;
    }

在第一次实例化对象后,以后再调用个getInstance方法时,就不会再创建对象,而且也不会再去获取锁,因为,第一个if判断语句都不会进去,所以不会执行到加锁的语句;

上面的单例模式看着好像是完全没问题了,但是,还是有一个问题,就是可能会触发指令重排序问题,所以就需要使用volatile解决指令重排序问题