JavaWeb——都来瞅瞅多线程的单例模式(饿汉模式、懒汉模式及其优化、单例模式和线程安全)

应用场景

代码中有的概念,不应该存在多个实例,此时应该使用单例模式。(JDBC中在DataSource这样的类中,在一个程序中只有一个实例,不应该实例化多个DataSiurce对象)

实现

保证指定的类只能有一个实例(如果有创建多个实例就会报错)

饿汉模式

饿汉模式,”饿“是值只要类被加载,实例就会立刻创建(实例创建时机比较早)

public class ThreadDemo {
    //创建类
    //饿汉模式
    static class Singleton{
        //把构造方法、变成私有,此时在此类外面就无法new这个类的实例
        //饿汉模式,”饿“是值只要类被加载,实例就会立刻创建(实例创建时机比较早)
        private Singleton(){
            //再创建一个static成员,表示Singleton类的唯一实例
            //static 和类相关,和实例无关,类在内存中只有一份,static成员也只有一份
        }
        private static Singleton instance = new Singleton();
            public static Singleton getInstance(){
                return instance;
            }
        public static void main(String[] args) {
            //getuinstance()是获取实例的唯一方式
            Singleton singleton = Singleton.getInstance();
            Singleton singleton1 = Singleton.getInstance();
            System.out.println(singleton == singleton1);
        }
    }
}

结果:true,两个对象获得的是同一个对象

懒汉模式

一般认为,懒汉模式比饿汉模式效率更高,懒汉模式有很大的可能是“实例用不到”,此时就节省了实例化的开销。


public class TheadDemo {
    //使用懒汉模式进行实现Singleton 类被加载的时候,不会立刻实例化
    //等到第一次使用这个实例的时候再进行实例化
    static class Singleton{
        private Singleton(){}

        private static Singleton instance = null; //类加载的时候没有实例化
        public static Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();//第一次调用getInstance时才能实例化
            }//如果代码一整场都没有调用getInstance,此时实例化的过程也就省略了
            return instance;
        }
    }
}

三次优化(使线程安全)

1、加锁
要使判断和修改保持原子性,
在这里插入图片描述
在这里插入图片描述
上面两个方法相当于接孩子放学,一个在教室门口等,一个在学校门口等。
锁中包含的代码越多,粒度越大,锁的粒度越小越好,粒度越大,说明代码的原子操作就多了,很多本来线程安全的代码没必要再去保证他的安全

2、只要在第一次的实例化之前调用的时候加锁
懒汉模式在实例化之前是存在线程不安全的,但是如果在实例化之后,后面再去并发执行调用getInstance就是线程安全的,在上面的代码中,即使实例已经创建好了,每次调用getInstance还会涉及加锁解锁,而在这里的加锁解锁其实已经不必要了

所以只要在第一次的实例化之前调用的时候加锁,后面不加锁。
在这里插入图片描述
3、目前加锁之后,会存在内存可见性问题
当一个线程被加锁修改后,另外一个线程读取的时候,存在两次读取,只有第一次读取才从内存中读取,后面的是从CPU中读取寄存器(上次读到的结果)
在这里插入图片描述
最终优化,在instance之前加上volatile
在这里插入图片描述

public class TheadDemo {
    //使用懒汉模式进行实现Singleton 类被加载的时候,不会立刻实例化
    //等到第一次使用这个实例的时候再进行实例化
    static class Singleton {
        private Singleton() {
        }

        private volatile static Singleton instance = null; //类加载的时候没有实例化

         public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();//第一次调用getInstance时才能实例化
                    }//如果代码一整场都没有调用getInstance,此时实例化的过程也就省略了

            }
        }
            return instance;
        }
    }
}

单例模式和“线程”关系

两种单例模式的实现方式是否是线程安全的

饿汉是线程安全
类加载只有一次机会,不可能并发执行,多线程同时调用getInstance,由于getInstance只做了一件事,就是读取instance实例的地址=》多个线程在同时读取同一个变量,没有修改。

饿汉模式不涉及多线程同时修改同一个变量,所以是安全的。

懒汉是线程不安全
多线程同时调用getInstance,由于getInstance做了四件事:
1、就是读取instance的内容
2、判断instance是否为null
3、如果instance为null,就会new实例,会修改instance的值
4、返回实例地址

在这里插入图片描述
懒汉模式在实例化之前是存在线程不安全的,但是如果在实例化之后,后面再去并发执行调用getInstance就是线程安全的。

线程不安全

1、线程的调度是抢占式执行
2、修改操作不是原子的
3、多线程同时修改同一个变量
4、内存可见性
5、指令重排序

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值