zoukankan      html  css  js  c++  java
  • (一)单例模式

    1,单例模式:

              为了限制该类对象被随意的创建,需要保证该类构造方法是私有的,这样外部类就无法创建该类型的对象了,另外,为了方便给客户对象提供对此单例对象的使用,给它提供一个全局访问点。

         2,实现方式:

      通常单例模式在Java语言中,有两种构建方式:

    • 懒汉式—线程不安全:最基础的实现方式,线程上下文单例,不需要共享给所有线程,也不需要加synchronize之类的锁,以提高性能。
    • 懒汉式—线程安全:加上synchronize之类保证线程安全的基础上的懒汉模式,相对性能很低,大部分时间并不需要同步
    • 饿汉方式。指全局的单例实例在类装载时构建。
    • 双检锁式。在懒汉式基础上利用synchronize关键字和volatile关键字确保第一次创建时没有线程间竞争而产生多个实例,仅第一次创建时同步,性能相对较高
    • 登记式。作为创建类的全局属性存在,创建类被装载时创建
    • 枚举。java中枚举类本身也是一种单例模式  

       3,单例中懒汉和饿汉的本质区别在于以下几点:

        (1)饿汉式: 线程安全的,在类创建的同时就已经创建好一个静态对象供系统使用,以后不再改变。

            懒汉式:如果在创建实例对象时不加上synchronized则会导致对象的访问不是线程安全的。

      (2)实现方式上划分:

             懒汉式是延迟加载,它在需要的时候才会创建对象,而饿汉式是在虚拟机启动的时候就会创建。

             饿汉式无需关注多线程问题,写法简单明了,能用则用。

       注意:它是加载类时创建实例,所以如果是工厂模式,缓存了很多实例,那么就得考虑效率问题,因为这个类一加载则把所有实例不管用不用都一块创建。

        (3)两者建立单例对象的时间不同。“懒汉式”是在你真正用到的时候才去建这个单例对象,“饿汉式”是在不管用不用得上,一开始就建立这个单例对象。

       4,优缺点对比:

        饿汉式:没有加锁,执行效率会提高。但是类加载时就初始化,浪费内存。它基于 classloder 机制避免了多线程的同步问题,

        懒汉式:线程不安全。

     5,简单的单例模式

         (1)饿汉模式:      

    public class HungrySingleton {
        //保存该类对象的实例,饿汉式的做法:在声明的同时初始化该对象
        private static HungrySingleton instance=new HungrySingleton();
        //将构造函数私有化,不对外提供构造函数
        private HungrySingleton(){}
        //对外提供访问该类对象的方法
        public static HungrySingleton getInstance(){
            return instance;
        }
    }

        (2)懒汉模式:

              出于性能等方面的考虑,采用延迟实例化单例对象(static属性在加载类时就被初始化),只有第一次使用该类的实例时才去实例化。-------------延迟创建

    /**
     * 非线程安全的懒汉式
     */
    public class UnThreadSafeLazySingleton {
        private static UnThreadSafeLazySingleton singleton=null;
        private UnThreadSafeLazySingleton(){}
        public static UnThreadSafeLazySingleton getInstance(){
               if(singleton==null){
                   singleton=new UnThreadSafeLazySingleton();
               }
               return singleton;
        }
    }

      6,线程不安全的解决办法:    

             在高并发环境中,getInstance()方法返回的多个指向不同该类的实例,这样在没有自动内存回收的平台会产生内存泄漏。

      (1) 加同步锁--------synchronized 同步方法

    public class TheadSafeSingelton {
        private static TheadSafeSingelton singleton=null;
        private TheadSafeSingelton(){}
        public static synchronized TheadSafeSingelton getInstance(){
            if(singleton==null){
                singleton=new TheadSafeSingelton();
            }
            return singleton;
        }
    }

     (2)加同步块------双重检测  Double-Check Locking

         synchronized 是重量级锁,在高并发访问情况下,很影响性能,所以缩小锁的范围来提高性能    

    // 双重检测 
    public class DoubleCheckSingleton {
        private volatile static DoubleCheckSingleton singleton=null;
        private DoubleCheckSingleton(){}
        public static DoubleCheckSingleton getInstance(){
            if(singleton==null){//检查是否已经被创建
                synchronized (DoubleCheckSingleton.class){ //同步块
                    if (singleton==null){//再次检测是否被创建----双重检测
                        singleton=new DoubleCheckSingleton();
                    }
                }
            }
            return singleton;
        }
    }

         new Singleton()分为三步,1、分配内存空间,2、初始化对象,3、设置instance指向被分配的地址。然而指令的重新排序,可能优化指令为1、3、2的顺序。如果是单个线程访问,不会有任何问题。但是如果两个线程同时获取getInstance,其中一个线程执行完1和3步骤,此时其他的线程可以获取到instance的地址,在进行if(instance==null)时,判断出来的结果为false,导致其他线程直接获取到了一个未进行初始化的instance,这可能导致程序的出错。所以用volatile修饰instance,禁止指令的重排序,保证程序能正常运行。(Bug很难出现,没能模拟出来)。

     参考地址:volatile

    (3) 既要保证线程安全又要延迟加载的效果还可以采用 持有者按需初始化模式(Initilization on demand holer

    /**
     * Initialization on demand holer 持有者按需初始化
     * @Author: wxw
     * @create: 2019-10-20-20:53
     * 线程安全的延迟加载的单例初始化
     */
    public class LazyLoadedSingleton {
        private LazyLoadedSingleton(){}
        private static class LazyHolder{ //持有单例类
            private static final LazyLoadedSingleton singleton=new LazyLoadedSingleton();
        }
        public static LazyLoadedSingleton getInstance(){
            return LazyHolder.singleton;
        }
    }

    说明:由于Jvm加载 LazyLoadedSingleton 类时,该类没有static的属性,所以加载完成后可以返回,只有第一次使用getInstance()方法时,Jvm才会加载LazyHolder类,由于它包含了一个static属性的singleton实例

     所以会首先初始化这个变量,这样并不会出现并发问题,这样既保证了线程安全又支持懒加载的单例模式。

    7,单实例singleton的序列化问题

    (1)问题: 如果单例类实现了Seriaizable接口,在默认情况下每次反序列化都会创建一个新的实例对象,这样一个系统会使用多个对象共使用。

           解决方案:在readResolve()方法中替换掉反序列化出来的那个新实例,让其指向内存中那个单例对象即可。

    (2)readResolve()方法:此方法在反序列化完成之前执行

    public class SerialibleSingleton implements Serializable {
         private static SerialibleSingleton singleton=new SerialibleSingleton();
         private SerialibleSingleton(){}
         public static SerialibleSingleton getInstance(){
             return singleton;
         }
         //反序列化完成之前执行,为了替换掉反序列化出来的那个新实例
         private Object readResolve(){
             return singleton;
         }
    }

    9,单例模式优缺点:

       优点: 
        1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例 
        2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 
        3.提供了对唯一实例的受控访问。 
        4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 
        5.允许可变数目的实例。 
        6.避免对共享资源的多重占用。 
    缺点: 
        1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 
        2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。 
        3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 
        4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。 
    使用注意事项: 
        1.使用时不能用反射模式创建单例,否则会实例化一个新的对象 
        2.使用懒单例模式时注意线程安全问题 
        3.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式) 

    参考博客:1,线程安全分析单例

                      2,单例模式概述

                      3,单例模式优缺点对比

                      4, 一个线程安全的、无需synchronization的、且比无竞争的同步高效的单例模式

    Java半颗糖
  • 相关阅读:
    2017-2018-1 20145237、20155205、20155218 实验一 开发环境的熟悉
    20145237 《信息安全系统设计基础》第四周学习总结
    20145237 《信息安全系统设计基础》第三周学习总结
    2017-2018-1 20145237《信息安全系统设计基础》第二周考试
    2017-2018-1 20145237 《信息安全系统设计基础》第1周学习总结
    20145237《信息安全系统设计基础》第5周学习总结
    20145237《信息安全系统设计基础》第5周学习总结(1)
    20145237 《信息安全系统设计基础》第3周学习总结
    20145235《信息安全系统设计基础》实验四 外设驱动程序设计
    20145235《信息安全系统设计基础》第十二周学习总结
  • 原文地址:https://www.cnblogs.com/2019wxw/p/11710289.html
Copyright © 2011-2022 走看看