• 4.设计模式---单例模式(上)


    单例模式有一下特点:
      1、单例类只能有一个实例。
      2、单例类必须自己自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。

    根据上面单利的特点,编写一个单利模式其实很简单:

    饿汉:

       

     1 package single.method;
     2 /**
     3  * 饿汉
     4  * @author zengrong
     5  *
     6  */
     7 public class eSingle {
     8     /**
     9      * 在这个类被加载时,静态变量single会被初始化,
    10      * 此时类的私有构造子会被调用。这时候,单例类的唯一实例就被创建出来了。
    11      */
    12     private static eSingle single =new eSingle();
    13     /**
    14      * 私有化构造函数
    15      */
    16     private eSingle(){}
    17     /**
    18      * 对外界唯一的公开生成实例的方法
    19      * @return
    20      */
    21     public eSingle getInESingle() {
    22         return single;
    23         
    24     }
    25     
    26     
    27     
    28     
    29 }

    饿汉式和他的名字一样:类一加载就生成实例:

    饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。

    懒汉:(线程不安全模式)

    
    

    package single.method;
    /**
    * 懒汉式(线程不安全)
    * @author zengrong
    *
    */
    public class lSingle {

    
    

    private static lSingle single=null;

    private lSingle(){}

    /**
    *类加载时候并不生成实例,当调用时候才去加载生成实例
    * @return
    */
    public static lSingle getLSingle() {
    return single==null ?new lSingle():single;
    }
    }

     

    懒汉式:时间换取空间,在类加载时候并不加载生成实例,但是在多线程情况下,二个线程几乎同时到达getLSingle()方法,这样就会产生了多个实例,违背了我们当初的想法。

    解决办法,双重加锁:

     1 package single.method;
     2 /**
     3  * 改造懒汉模式变成线程安全
     4  * @author zengrong
     5  *
     6  */
     7 public class lSingleSafety {
     8     /**
     9      * 用volatile修饰的变量,
    10      * 线程在每次使用变量的时候,
    11      * 都会读取变量修改后的最的值。
    12      * volatile很容易被误用,用来进行原子性操作。
    13      */
    14     private volatile static lSingleSafety singleSafety=null;
    15     
    16     
    17     private lSingleSafety(){}
    18     /**
    19      * 采用的是双重加锁进行效率和安全
    20      * @return
    21      */
    22     public static lSingleSafety getLSingleSafety() {
    23         
    24         if(singleSafety==null){
    25             synchronized (lSingleSafety.class) {
    26                  if(singleSafety == null){
    27                      singleSafety= new lSingleSafety();
    28                  }
    29             }
    30         }
    31         return singleSafety;
    32     }
    33     
    34 }

    所谓“双重检查加锁”机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

      “双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

      注意:在java1.4及以前版本中,很多JVM对于volatile关键字的实现的问题,会导致“双重检查加锁”的失败,因此“双重检查加锁”机制只只能用在java5及以上的版本。

    在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:

      1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时

      2.访问final字段时

      3.在创建线程之前创建对象时

      4.线程可以看见它将要处理的对象时

    • 类级内部类?

      简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。

      类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。

      类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。

      类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。

    根据上面的这些:

    那么我们是不是可以在创建一个线程安全且又不用同步锁:

     1 package single.method;
     2 /**
     3  * 使用类级别的静态类部类来进行实例化
     4  * @author zengrong
     5  *
     6  */
     7 public class StaticSingle {
     8 
     9     private StaticSingle(){}
    10     
    11     private static class SingleOnInside{
    12         /**
    13          * 初始化静态
    14          */
    15     private static StaticSingle single=new StaticSingle();
    16         
    17     }
    18     
    19     /**
    20      * 安全问题由JVM来保证
    21      * @return
    22      */
    23     public static StaticSingle getStaticSingle() {
    24         
    25         return SingleOnInside.single;
    26     }
    27     
    28 }

    最牛逼的方法:高效,安全

    枚举:

    package single.method;
    /**
     * 枚举方法
     * @author zengrong
     *
     */
    public enum Singleton {
    
          /**
         * 定义一个枚举的元素,它就代表了Singleton的一个实例。
         */
        
        uniqueInstance;
        
        /**
         * 单例可以有自己的操作
         */
        public void singletonOperation(){
            //功能处理
        }
        
    }

    单例模式的应用场景:

    其中有些我也没用过,从其他博客看到的,记录下:

    1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 

    2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

    3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。

    4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

    5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

    6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。

    7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

    8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

    9. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.

    放在JavaWeb里讲,对于一个web项目,servletContext这个上下文对象就是单例模式的体现,因为它对应着配置文件,是全局所共享的,jsp的内置对象application也是这个道理。

    对于枚举的单利模式明天继续详细分析;睡觉睡觉。。。。。

  • 相关阅读:
    c# 合并byte数组
    DataGridView扩展方法行号、全选、导出到Excel(引用excel组件、生成html两种方式)
    c#利用zlib.net对文件进行deflate流压缩(和java程序压缩生成一样)
    TSQL查询笔记4: FROM T1,T2与联接的区别
    “菜鸟”程序员和“大神”程序员差距在哪里
    JAVA:模板方法模式
    Windows检测到一个硬盘问题?
    照我思索,你的电脑百毒不侵 (转)
    JAVA:多态
    HTML与CSS(图解4):表格
  • 原文地址:https://www.cnblogs.com/java-synchronized/p/6671303.html
Copyright © 2020-2023  润新知