多线程-单例模式与多线程(三)

实现单例模式有3种方式:

  1. 立即加载/饿汉模式:单例实例在类装载时创建
    饿汉模式是线程安全的
    不足:
    1、如果构造方法中存在过多的处理,会导致类加载很慢,造成性能问题
    2、如果只进行类的加载没有实际调用,会造成资源浪费
  2. 延迟加载/懒汉模式:单例实例在第一次使用时进行创建
  3. 枚举模式:相较于懒汉模式线程更能保证线程安全,相较于饿汉模式是在实际调用时才被初始化(推荐使用)

实现单例模式线程安全方式:

  1. 方式一:饿汉模式

    /**
     * 单例模式
     * 饿汉模式:单例实例在类装载时创建
     */
    public class Singleton01 {
    
        private Singleton01(){}
    
        //初始化方式一:静态域
        //private static Singleton01 instance = new Singleton01();
    
        //初始化方式二:静态代码块:可以在真正创建实例化之前在静态代码块中做一些操作
        private static Singleton01 instance = null;
        static {
            instance = new Singleton01();
        }
    
        public static Singleton01 getInstance(){
            return instance;
        }
    }
    	//测试多线程下线程是否安全
    class TestSingleThread{
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Callable<Singleton01> c = new Callable<Singleton01>() {
                @Override
                public Singleton call() throws Exception {
                    return Singleton01.getInstance();
                }
            };
    
            //创建一个定长线程池,超出的线程会在队列中等待
            ExecutorService es = Executors.newFixedThreadPool(3);
            Future<Singleton01> f1 = es.submit(c);
            Future<Singleton01> f2 = es.submit(c);
            System.out.println(f1.get().hashCode());
            System.out.println(f2.get().hashCode());
            //结论:线程安全
        }
    }
    
  2. 方式二:懒汉模式

    /**
     * 单例模式
     * 懒汉模式:单例实例在第一次使用时进行创建
     */
    public class Singleton {
    
        private Singleton() {
        }
    
        //单例对象
        private static Singleton singleton = null;
        //private static volatile Singleton singleton = null;
    
        /*
         *线程安全方式一:同步代码块
         */
        public static Singleton getInstance(){
            //方式一:效率稍低:因为这里每个等待的线程都会去拿锁
    //        synchronized(Singleton.class){
    //            if(singleton==null){
    //                singleton = new Singleton();
    //            }
    //            return singleton;
    //        }
    
            /**
             * 方式二:双重同步锁单例模式
             * 但是这样实现也不完全是线程安全的,因为jvm和cpu优化,可能会导致指令重排
             * 效率稍高,因为在加锁之前判断,后面的线程就不用继续等待了
             */
    
            if(singleton==null){//双重检测机制
                synchronized(Singleton.class){//同步锁
                    if(singleton==null){//步骤a
                        /*
                         *新建一个Singleton内部执行的指令操作:
                         * 1、memory = allocate() 分配对象的内存空间
                         * 2、ctorInstance() 初始化对象
                         * 3、instance = memory 设置instance指向刚分配的内存
                         *
                         * jvm和cpu优化,可能会导致指令重排
                         * 1、memory = allocate() 分配对象的内存空间
                         * 3、instance = memory 设置instance指向刚分配的内存
                         * 2、ctorInstance() 初始化对象
                         *
                         * 这样会导致线程A拿到锁之后,进入到步骤b,指令重排之后先执行了3,此时singleton已经有值了,但是还并未初始化对象,
                         * 又有一个线程B执行到步骤a,判断到singleton不为null,则直接执行到步骤c,返回了并未初始化的singleton,则会造成问题
                         *
                         * 解决指令重排:给单例对象加上volatile
                         */
                        singleton = new Singleton();//步骤b
                    }
                }
            }
            return singleton;//步骤c
    
        }
    
        /*
         *线程安全方式二:同步方法,不推荐,会造成性能问题
         */
        public static synchronized Singleton getInstance1(){
            if(singleton==null){
                singleton = new Singleton();
            }
            return singleton;
        }
    
    
    }
    
  3. 方式三:枚举模式

    /**
     * 单例模式
     * 枚举模式:相较于懒汉模式线程更能保证线程安全,相较于饿汉模式是在实际调用时才被初始化
     * 推荐使用枚举模式
     */
    public class Singleton02 {
    
        private Singleton02(){}
    
        public static Singleton02 getInstance(){
            return Singleton.INSTANCE.getSingleton();
        }
    
        private enum Singleton{
            INSTANCE;
    
            private Singleton02 singleton = null;
    
            //jvm保证这个方法绝对只调用一次
            Singleton(){
                singleton = new Singleton02();
            }
    
            public Singleton02 getSingleton(){
                return singleton;
            }
        }
    
    }
    
  4. 多线程测试
    新建一个测试类:

    public class TestSingletonThread {
    
        public static void main(String[] args) {
            MyThread02 t1 = new MyThread02();
            MyThread02 t2 = new MyThread02();
            MyThread02 t3 = new MyThread02();
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    class MyThread02 extends Thread{
        @Override
        public void run() {
        	//这里调用单例模式的类,分别打印出的hashCode一致
            System.out.println(Singleton.getInstance().hashCode());
        }
    }
    

打印结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值