zoukankan      html  css  js  c++  java
  • 设计一个安全的单例模式

    https://blog.csdn.net/Evan_QB/article/details/83537766

    https://blog.csdn.net/liuchaoxuan/article/details/79840013 参考

    https://blog.csdn.net/weixin_42613513/article/details/84844317 线程命名测试运行

    饿汉式,安全的

    public class SingletonExample2 {
        //私有化构造函数
        private SingletonExample2(){
    
        }
        //单例对象
        private static SingletonExample2 instance = new SingletonExample2();
    
        //静态工厂方法
        public static SingletonExample2 getInstance(){
           return instance;
        }
    }
    

     

    懒汉式 不安全的

    public class SingletonExample1 {
        //私有化构造函数
        private SingletonExample1(){
    
        }
        //单例对象
        private static SingletonExample1 instance = null;
    
        //静态工厂方法
        public static SingletonExample1 getInstance(){
            if (instance == null){
                instance = new SingletonExample1();
            }
            return instance;
        }
    

     从写法上我们可以看出,饿汉模式是线程安全的,但它的性能上会大大折扣。那么我们能否也让懒汉模式也变得线程安全呢?答案是可以的 

    方法一.在方法上加上同步锁

    直接在获取实例的方法上加上synchronized关键字

    public class SingletonExample3 {
        //私有化构造函数
        private SingletonExample3(){
    
        }
        //单例对象
        private static SingletonExample3 instance = null;
    
        //静态工厂方法
        public synchronized static SingletonExample3 getInstance(){
            if (instance == null){
                instance = new SingletonExample3();
            }
            return instance;
        }
    }
    

      

    虽说该方法是线程安全的,但其性能也和饿汉模式差不多,在性能上会大大折扣,别急我们接着看

    方法二.使用双重校验加同步锁机制

    public class SingletonExample4 {
        //私有化构造函数
        private SingletonExample4(){
    
        }
        //指令重排问题:
        //1.分配内存空间
        //2.初始化对象
        //3.instance = memory设置instance指向刚分配的内存
    
        //单例对象
        private static SingletonExample4 instance = null;
    
        //静态工厂方法
        public static SingletonExample4 getInstance(){
            if (instance == null){  //双重检测机制
                synchronized (SingletonExample4.class) {    //同步锁
                    if (instance == null){
                        instance = new SingletonExample4();
                    }
                }
            }
            return instance;
        }
    }
    

      通过两次判断,确保创建的对象只能有一个,但这种方法还是存在线程安全的问题的。
    在单线程的情况下,以上的代码没有丝毫问题,但在多线程的情况下,就会存在指令重排问题

     当A、B线程达到以上位置时,发生指令重排,在A线程执行到指令2(将对象的引用指向新分配的空间)时,刚好CPU被B占用,这样B的对象指向了一个内存空间,但其对象并没有被实例化

    我们可以给代码加上一个volatile关键字来防止指令重排

    //单例对象
    private static volatile SingletonExample4 instance = null;
    

      

    方法三.使用枚举模式来创建对象

    有了以上方法为何还会需要第三种方法呢?那是因为java中还有一种暴力的创建方法,反射,虽然不能通过关键字new来创建对象,但通过反射创建的对象,就不会是单例的了,那么有什么办法可以解决吗?答案是有的,就是使用枚举。

    public class SingletonExample7 {
        private SingletonExample7(){}
    
        public static SingletonExample7 getInstance(){
            return Singleton.INSTANCE.getInstance();
        }
    
        private enum Singleton{
            INSTANCE;
    
            private SingletonExample7 singleton;
    
            //JVM保证这个方法绝对只调用一次
            Singleton(){
                singleton = new SingletonExample7();
            }
    
            public SingletonExample7 getInstance() {
                return singleton;
            }
        }
    }
    

      

    4、使用静态内置类实现单例模式

    DCL解决了多线程并发下的线程安全问题,其实使用其他方式也可以达到同样的效果,代码实现如下:

    package org.mlinge.s06;  
      
    public class MySingleton {  
          
        //内部类  
        private static class MySingletonHandler{  
            private static MySingleton instance = new MySingleton();  
        }   
          
        private MySingleton(){}  
           
        public static MySingleton getInstance() {   
            return MySingletonHandler.instance;  
        }  
    }  
    

      

     测试1:继承线程

    public class Singleton {
        //防止指令重排
        private static volatile Singleton singleton =null;
        private Singleton(){
        }
        public static Singleton getSingleton() {
                if (singleton == null) {//双重检测
                    synchronized (Singleton.class) {
                        if (singleton==null) {
                            singleton = new Singleton();
                        }
                    }
                }
    
            return singleton;
        }
    }
    
    public class SingletonTest extends  Thread{
    
        public static void main(String ss[]){
            Thread[] tms = new Thread[10];
            for (int i = 0;i<tms.length;i++){
                SingletonTest singletonTest = new SingletonTest();
                singletonTest.setName("线程"+i);
                tms[i] = singletonTest;
            }
            for (int j = 0;j<tms.length;j++){
                tms[j].start();
            }
        }
    
        @Override
        public void run(){
            System.out.println(Thread.currentThread().getName()+"_"+Singleton.getSingleton().hashCode());
        }
    

      

    测试2:实现runable

    public class Singleton {
        //防止指令重排
        private static volatile Singleton singleton =null;
        private Singleton(){
        }
        public static Singleton getSingleton() {
                if (singleton == null) {//双重检测
                    synchronized (Singleton.class) {
                        if (singleton==null) {
                            singleton = new Singleton();
                        }
                    }
                }
    
            return singleton;
        }
    }
    
    public class SingletonRunableTest implements Runnable {
    
        public static void main(String ss[]){
            Thread[] tms = new Thread[10];
            for (int i = 0;i<tms.length;i++){
                SingletonRunableTest test = new SingletonRunableTest();
                tms[i] = new Thread(test,"线程"+i);
            }
            for (int j = 0;j<tms.length;j++){
                tms[j].start();
            }
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"_"+Singleton.getSingleton().hashCode());
        }
    
    }
    

      

  • 相关阅读:
    微信转发或分享朋友圈带缩略图、标题和描述的实现方法
    apache一个IP多个站点的配置方法
    微信网页扫码登录的实现
    laravel take(3) 读取最近三条信息
    微信卡劵、微信卡包,必须是认证订阅号或认证服务号
    CSS3 去除苹果浏览器按钮input[type="submit"]和input[type="reset"]的默认样式
    使用laravel5.4结合easywechat进行微信开发--基本配置
    Class 'QrCode' not found ? 和 laravel 生成二维码接口(Simple QrCod)
    windows redis的启动 和 Laravel中Redis的使用
    改变checkbox的默认样式
  • 原文地址:https://www.cnblogs.com/Andrew520/p/11484809.html
Copyright © 2011-2022 走看看