zoukankan      html  css  js  c++  java
  • 单例模式浅析

     

    浅析单例模式

    单例模式概述:

    单例模式,我觉得是多种设计模式中最简单、也是应用最多的设计模式。顾名思义,单例模式指的是该类的对象实例为全局唯一变量,也可以说,一个类只有一个实例对象。
    具体实现步骤:
    1..构造方法私有化 。
    2.在类内部创建私有静态对象实例。
    3.提供公有获取唯一实例的方法。。

    常见的单例模式写法:

    饿汉式:

    public class singleton {
        //构造方法私有化
        private singleton(){
        }
        //创建私有实例对象
        private   static  final singleton instance=new singleton();
        //公有方法获取唯一实例
        public static singleton  getInstance(){
            return  instance;
        }
    }

    饿汉式,在类加载的时候就会创建唯一的实例对象供外部使用,优点是线程安全。缺点是类加载就创建实例,可能造成内存的浪费。

    懒汉式(延迟加载方式):

    public class singleton {
         private singleton(){
         }
         //实例化对象先为空
         private static  singleton  instance=null;
         //先判断对象是否对象是否为空,如果为空,才开始创建实例化对象。
         public static singleton getInstance(){
             if(instance==null){
                 instance=new singleton();
             }
             return instance;
         }
    
    }

    懒汉式,采用延迟加载的方式,即类加载的时候并不会创建实例化对象,只有真正用到这个对象,调用getInstance()方法的时候才会去实例化这个对象,所以叫做懒汉式。
    懒汉式方法的优点是,并不会浪费系统内存。缺点是并不是线程安全的。
    那为了实现线程安全,我们自然会想到使用synchronized同步锁。那到底行不行呢?
    懒汉式使用synchronized同步锁:

    public class singleton {
        //构造方法私有化
        private singleton(){
        }
        //实例化对象先为空
        private static  singleton  instance=null;
        //为该方法加锁,保证线程安全。
        public static  synchronized singleton getInstance(){
            if(instance==null){
                instance=new singleton();
            }
            return instance;
        }
    
    }

    这样做导致的问题是在多线程情况下会导致性能比较低下。那我们可以考虑一下,是不是可以把锁的范围缩小呢?

    所以代码可以这样写:

    public class singleton {
        //构造方法私有化
        private singleton(){
        }
        //实例化对象先为空
        private static  singleton  instance=null;
        //为该方法加锁,保证线程安全。
        public static   singleton getInstance(){
            if(instance==null){
                synchronized(singleton.class) {
                    instance = new singleton();
                }
            }
            return instance;
        }
    }

    表面看上去没什么问题,其实仔细考虑我们就会发现可能会出现两个实例化对象的问题。
    考虑这样的情况。线程A和线程B同时进入到getInstance()方法里。都判断instance==null,所以都进入下面的if()代码块了。
    假设线程A得到cpu使用权,进入到同步代码块,创建了对象并返回对象。当线程A完成以后,此时线程B继续进入同步代码块,就会创建另一个对象并返回。
    所以这样的话就会返回不止一个对象实例啦。
    我们可以看一下测试代码的结果:
    enter description here

    所以为了解决这种问题,我们可以在锁内再判断一次是否对象是否为空。这就是DCL(双重检测机制)懒汉式。

    其实这样还不完全稳,会有重排序的问题,这里就不细说,感兴趣的自己了解。要解决重排序也很简单,使用volatile关键字,具有内存屏障的功能。下面是完整的代码:

    双重检测机制(DCL)懒汉式:

    public class singleton {
        //构造方法私有化
        private  singleton(){
        }
        //实例化对象先为空
        private static volatile singleton  instance=null;
        //为该方法加锁,保证线程安全。
        public static   singleton getInstance(){
            if(instance==null){
                //锁范围缩小
                synchronized(singleton.class) {
                    //再次判断是否为空
                    if(instance==null) {
                        instance = new singleton();
                    }
                }
            }
            return instance;
        }
    
    }

    另外一种推荐使用的方法就是静态内部类的方法:
    静态内部类:

    public class singleton {
        //构造方法私有化
        private singleton(){
        }
        //私有静态内部类
        private static class LazyHolder{
            //创建单例对象
            private static final singleton instance=new singleton();
        }
        //获取单例对象
        public static final singleton getInstance(){
            return LazyHolder.instance;
        }
    
    }

    该方法的原理是多线程情况下,不管哪个线程第一次调用getInstance方法都会LazyHolder被加载和初始化,而当初始化静态数据时,java是线程安全的。

    最后一种实现方式比较简单,使用枚举:
    枚举方法:

    public enum Singleton {  
        instance;  
        public void whateverMethod() {  
        }  
    }

    这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
    由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

  • 相关阅读:
    SQL server 插入不同IP的数据库
    SQL Server中的循环例子(网摘)
    C#小型数据库只能查询
    vue.prototype和vue.use的区别和注意点
    Ajax+PHP简单入门教程
    smarty在windows下的安装
    docker安装mysql镜像和容器
    Linux导出未越狱Iphone10.3QQ聊天记录
    记一次Struts中文乱码
    Ubuntu设置服务开机启动
  • 原文地址:https://www.cnblogs.com/fankailei/p/10567605.html
Copyright © 2011-2022 走看看