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

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。JavaWeb——都来瞅瞅多线程的单例模式(饿汉模式、懒汉模式及其优化、单例模式和线程安全),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

应用场景

代码中有的概念,不应该存在多个实例,此时应该使用单例模式。(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、指令重排序

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/152975.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!