zoukankan      html  css  js  c++  java
  • java多线程与单例模式(Singleton)不得不说的故事

    转发自:http://blog.csdn.net/ligang7560/article/details/50890282

    单例模式的多种实现方式

    我们都知道单例模式有几种常用的写法:
    - 饿汉模式
    - 懒汉模式
    - 双重校验锁
    - 静态内部类
    - 静态代码块
    我们来看一下这几种模式在多线程的场景中,能否保持单例

    1.饿汉模式

    public class HungrySingleton {
    
        //立即加载模式
        private static HungrySingleton hungrySingleton = new HungrySingleton();
    
        private HungrySingleton(){}
    
        public static HungrySingleton getInstance(){
            return hungrySingleton;
        }
    
    }
    
    

    接下来我们写一个线程类,再写一个测试类,来看下饿汉模式在多线程下的表现如何:这里写图片描述

    运行测试类结果如下:

    	546862187
    	546862187
    	546862187
    

    这说明饿汉模式是线程安全,在多线程的情况下,也能够保持单例.

    2.懒汉模式

    public class LazySingleton {
    
        private static LazySingleton lazySingleton;
    
        private LazySingleton() {}
    
        public static LazySingleton getInstance(){
            if(lazySingleton!=null){
            }else{
                lazySingleton =  new LazySingleton();
            }
            return lazySingleton;
        }
    
    
    }
    

    同样的我们来写一个线程类,写一个测试类;
    这里写图片描述
    运行结果:

    	2030251396
    	546862187
    	2030251396
    

    我们看到对象返回的哈希值不一致了,这就说明他们已经不是同一个对象实例了,也就是说这种写法是线程不安全的,那么我们对它进行如下的改造:

    public class LazySingleton {
    
        private static LazySingleton lazySingleton;
    
        private LazySingleton() {}
    
        //整个方法加同步,效率低
        synchronized public static LazySingleton getInstance(){
            if(lazySingleton!=null){
            }else{
                lazySingleton =  new LazySingleton();
            }
            return lazySingleton;
        }
    }
    

    我们给整个getInstance方法添加了个synchronized关键字,才保证多线程的情况下也能保持单例,我们再运行一下测试类:

    1638084561
    1638084561
    1638084561
    

    这时我们看见已经没有问题了,就是说写法是正确的,但是这种写法导致所以并发的线程获取实例时都是排队进行的,那么就会导致性能低下,接下来我们尝试另外一种写法,就是我们的第三种写法:双重校验锁.

    3. 双重校验锁

    public class LazySingleton {
    
        private static LazySingleton lazySingleton;
    
        private LazySingleton() {}
    
         public static LazySingleton getInstance(){
            if(lazySingleton!=null){
            }else{
                synchronized (LazySingleton.class){
                    if(lazySingleton==null){
                        lazySingleton =  new LazySingleton();
                    }
                }
    
            }
            return lazySingleton;
        }
    }
    

    我们将排队进行的范围进行缩小,同时采用校验两次为空的方法,来保证单例模式的正确性,接下来我们来看一下测试结果:

    2030251396
    2030251396
    2030251396
    

    OK,没有问题.

    4.静态内部类

    public class StaticInnerClassSingleton {
    	//通过写一个静态的内部类来创建实例
        private static class SingletonHandle{
            private static StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
        }
    
        private StaticInnerClassSingleton(){}
    
        public static StaticInnerClassSingleton getInstance(){
            return SingletonHandle.singleton;
        }
    
    }
    

    测试类和上面的都差不多,我就不贴图片了,直接看测试结果:

    1523917841
    1523917841
    1523917841
    

    没有问题,这种方式也是线程安全的,接下来我们看最后一种方式

    5.静态代码块

    public class StaticSingleton {
    
        private static StaticSingleton staticSingleton=null;
    
        private StaticSingleton(){};
    	
        static {
    	//通过静态代码块的执行,来获取实例
            staticSingleton = new StaticSingleton();
        }
    
        public static StaticSingleton getInstance(){
            return staticSingleton;
        }
    }
    

    不废话了,直接看测试结果:

    2030251396
    2030251396
    2030251396
    

    没有问题,那么以上我们就介绍了5中关于多线程中单例模式的写法,其中呢就懒汉模式的一般写法容易造成错误,其他的写法都是线程安全的,文章中的代码我已经上传到github了,有需要的同学可以下载下来自己试试.
    https://github.com/JokerLigang/Singleton.git

  • 相关阅读:
    python之路day10命名空间和作用域、函数嵌套,作用域链、闭包
    20200719 千锤百炼软工人第十四天
    20200720 千锤百炼软工人第十五 天
    JavaScript中基本数据类型和引用数据类型的区别
    js 经纬度坐标格式化 度分秒转度 度转度分秒
    export ,export default 和 import 区别 以及用法
    10月28日学习日志
    10月20日学习日志
    10月29日学习日志
    10月27日学习日志
  • 原文地址:https://www.cnblogs.com/j2eeDevelper/p/5286158.html
Copyright © 2011-2022 走看看