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

    一:单例模式的优点

              --->单例类只能有一个实例

                 --->单例类必须自己创建自己的唯一实例。

                 --->单例类必须给所有其他对象提供这一实例。

    二:单例模式分类

      --->饿汉模式

            --->懒汉模式


    三:单例模式应用场景

            --->

    四:单例模式的角色

            --->
    五:单例模式的代码示例

    饿汉模式:

     1 package com.yeepay.sxf.interfaces.impl;
     2 /**
     3  * 饿汉单例模式
     4  * 是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
     5  * @author sxf
     6  *
     7  */
     8 public class SingObj {
     9     /**
    10      * 持有自己的引用
    11      */
    12     private static SingObj singObj=new SingObj();
    13     /**
    14      *似有化构造器
    15      */
    16     private SingObj(){
    17         
    18     }
    19     /**
    20      * 提供静态方法返回自身对象
    21      * @return
    22      */
    23     public static SingObj getSingObj(){
    24         return singObj;
    25     }
    26 }
    View Code

    懒汉模式:

    (1)线程安全的懒汉模式(并发速度缓慢)

     1 package com.yeepay.sxf.interfaces.impl;
     2 /**
     3  * 懒汉模式
     4  * 
     5  * (1)懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间
     6  * (2)由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式实现呢?
     7  *
     8  */
     9 public class SingObj2 {
    10     /**
    11      * 持有自身引用
    12      */
    13     private static SingObj2 singObj2=null;
    14     
    15     /**
    16      * 构造器私有化
    17      */
    18     private void  SingObj2(){
    19         
    20     }
    21     
    22     /**
    23      * 提供静态方法返回对象.同步方法,防止出现多个对象,违反单例规则
    24      * @return
    25      */
    26     public static synchronized SingObj2 getSingObj2(){
    27         if(singObj2==null){
    28             singObj2=new SingObj2();
    29         }
    30         return singObj2;
    31     }
    32 }
    View Code

    (2)线程安全的懒汉模式(并发速度提高)

     1 package com.yeepay.sxf.interfaces.impl;
     2 /**
     3  * 懒汉模式升级版
     4  * 
     5  * @author sxf
     6  *
     7  */
     8 public class SingObj3 {
     9     /**
    10      * 持有自身对象的引用
    11      */
    12     private static SingObj3 singObj3=null;
    13     
    14     /**
    15      * 构造器似有化
    16      */
    17     private void SingObj3(){
    18         
    19     }
    20     /**
    21      * 升级版的静态方法返回单例对象引用。只需要第一次创建时才用到同步。以后获取就不需要同步
    22      * @return
    23      */
    24     public static SingObj3 getSingObje3(){
    25         if(singObj3==null){
    26             synchronized (SingObj3.class) {
    27                 if(singObj3==null){
    28                     singObj3=new SingObj3();
    29                 }
    30             }
    31         }
    32         
    33         return singObj3;
    34     }
    35 
    36 }
    View Code

    (3)内部类的懒汉模式

     1 package com.yeepay.sxf.interfaces.impl;
     2 /**
     3  * 懒汉模式的再次升级版
     4  * 使用类级内部类
     5  * 什么是类级内部类?
     6  *       (1)简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。
     7   (2)类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
     8   (3)类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
     9       (4)类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载
    10  * 
    11  * 多线程缺省同步所的机制
    12  * 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
    13 
    14   1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
    15 
    16   2.访问final字段时
    17 
    18   3.在创建线程之前创建对象时
    19 
    20   4.线程可以看见它将要处理的对象时
    21  * @author sxf
    22  *
    23  */
    24 public class SingObj4 {
    25     /**
    26      * 构造器似有化
    27      */
    28     private SingObj4(){}
    29     /**
    30      *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
    31      *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
    32      *    一旦加载,不会再次加载
    33      */
    34     private static class SingletonHolder{
    35         /**
    36          * 静态初始化器,由JVM来保证线程安全
    37          */
    38         private static SingObj4 instance = new SingObj4();
    39     }
    40     /**
    41      * 
    42      * @return
    43      */
    44     public static SingObj4 getInstance(){
    45         return SingletonHolder.instance;
    46     }
    47 }
    View Code

    (4)枚举的懒汉模式

     1 public enum Singleton {
     2     /**
     3      * 定义一个枚举的元素,它就代表了Singleton的一个实例。
     4      */
     5     
     6     uniqueInstance;
     7     
     8     /**
     9      * 单例可以有自己的操作
    10      */
    11     public void singletonOperation(){
    12         //功能处理
    13     }
    14 }
    View Code

    单例,大家肯定都不陌生,这是Java中很重要的一个设计模式。稍微了解一点单例的朋友也都知道实现单例是要考虑并发问题的,一般情况下,我们都会使用synchronized来保证线程安全。

    那么,如果有这样一道面试题:不使用synchronized和lock,如何实现一个线程安全的单例?你该如何回答?

    C类应聘者:可以使用饿汉模式实现单例。如:

    public class Singleton { 
         private static Singleton instance = new Singleton();
         private Singleton (){}
         public static Singleton getInstance() {
           return instance;
         }
    }
    View Code

    还有部分程序员可以想到饿汉的变种:

    public class Singleton {
         private Singleton instance = null;
         static {
             instance = new Singleton();
         }
         private Singleton (){}
         public static Singleton getInstance() {
             return this.instance;
         }
    }
    View Code

    使用static来定义静态成员变量或静态代码,借助Class的类加载机制实现线程安全单例。

    面试官:除了这种以外,还有其他方式吗?

    B类应聘者:

    除了以上两种方式,还有一种办法,就是通过静态内部类来实现,代码如下:

    public class Singleton {
         private static class SingletonHolder {
             private static final Singleton INSTANCE = new Singleton();
         }
         private Singleton (){}
         public static final Singleton getInstance() {
             return SingletonHolder.INSTANCE;
         }
    }
    View Code

    这种方式相比前面两种有所优化,就是使用了lazy-loading。Singleton类被装载了,但是instance并没有立即初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。

    面试官:除了这种以外,还有其他方式吗?

    A类应聘者:

    除了以上方式,还可以使用枚举的方式,如:

    public enum Singleton {
         INSTANCE;
         public void whateverMethod() {
         }
    }
    View Code

    这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒。

    面试官:以上几种答案,其实现原理都是利用借助了类加载的时候初始化单例。即借助了ClassLoader的线程安全机制。

    所谓ClassLoader的线程安全机制,就是ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。也正是因为这样, 除非被重写,这个方法默认在整个装载过程中都是同步的,也就是保证了线程安全。

    所以,以上各种方法,虽然并没有显示的使用synchronized,但是还是其底层实现原理还是用到了synchronized。

    面试官:除了这种以外,还有其他方式吗?

    A类应聘者:

    还可以使用Java并发包中的Lock实现

    面试官:本质上还是在使用锁,不使用锁的话,有办法实现线程安全的单例吗?

    A+类面试者:

    有的,那就是使用CAS。

    CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。实现单例的方式如下:

    public class Singleton {
         private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
    
         private Singleton() {}
    
         public static Singleton getInstance() {
             for (;;) {
                 Singleton singleton = INSTANCE.get();
                 if (null != singleton) {
                     return singleton;
                 }
    
                 singleton = new Singleton();
                 if (INSTANCE.compareAndSet(null, singleton)) {
                     return singleton;
                 }
             }
         }
    }
    View Code

    面试官:这种方式实现的单例有啥优缺点吗?

    A++类面试者:

    用CAS的好处在于不需要使用传统的锁机制来保证线程安全,CAS是一种基于忙等待的算法,依赖底层硬件的实现,相对于锁它没有线程切换和阻塞的额外消耗,可以支持较大的并行度。


    CAS的一个重要缺点在于如果忙等待一直执行不成功(一直在死循环中),会对CPU造成较大的执行开销。

    另外,如果N个线程同时执行到singleton = new Singleton();的时候,会有大量对象创建,很可能导致内存溢出。

    面试官:你被录取了!

  • 相关阅读:
    使用SQL查询所有数据库名和表名
    vue打包时给静态资源增加版本号
    mac笔记本好用的快捷键汇总
    jquery项目好用的插件汇总
    通过js禁止输入空格(试用场景:当用字符串拼接插入dom节点时,onkeyup这些方法都不好使可用这个)
    textarea和type=number输入去空格限制字数问题
    用websocket建立远程连接(vue)
    配置本地服务器
    webpack打包路径问题
    序列号和反序列化==》nodejs之querystring模块(尼玛,太强大,好用耶)
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/4641313.html
Copyright © 2011-2022 走看看