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

     单例模式是众多设计模式的一种。单例类可以保证其类型只会生成一个实例,只拥有一个实例在很多时候是很有用的,比如说全局访问以及缓存代价高昂的资源;不过如果在多线程环境下使用单例,那就可能引入一些竞态条件问题。由于大多数编程语言并没有提供创建单例的内置机制,因此需要开发者自己来实现。

    1 单例概述

      单例模式用于确保一个类只有一个实例,并且提供了实例的一个全局访问点。该模式通常与工厂模式配合使用。

      单例模式的一般应用场景:

      1)跨越整个应用程序域来访问共享数据,比如配置数据。

      2)只加载并缓存代价高昂的资源一次,这样就可以做到全局共享访问并改进性能。

      3)创建应用日志实例,因为通常情况下只需要一个即可。

      4)管理实现了工厂模式的类中的对象。

      5)延迟创建静态类,单例可以做到延迟实例化。

    延伸:Spring在创建Bean时使用了单例(默认情况下,SpringBean是单例的),在JavaEE内部会使用单例,比如在服务定位器中。javaSE也在Runtime类的实现中使用了单例模式。

      不过过度的使用单例模式意味着不必要的资源缓存,无法让垃圾回收器回收对象并释放宝贵的内存资源。当然这样做也无法利用对象创建和继承的好处。大量使用单例类可能会导致内存和性能问题。并且单例类的使用对测试并不友好。


     

    2 单例类的实现

    2.1 单例类的简单实现

    由于单例只有一个实例,因此要控制对象的创建。首先将构造方法进行私有化。接着要提供一个方法创建实例,并且保证该方法是静态的,如果实例已经存在,则返回它。

    package com.hzw.singleton;

    public class MySingleton{

      private static MySingleton instance;

      private MySingleton(){}

      public static MySingleton getInstance(){

        if(instance==null){

          instance = new MySingleton();

        }

        return instance;

      }

    }

    getInstance()方法首先会判断单例有没有创建,如果没有创建则创建它,否则,返回之前调用getInstance()方法创建的实例。后续的每次调用都是返回之前创建的MySingleton对象的实例,这段实现单例模式的代码看起来可以正常运行,不过在实际上却是存有BUG的而且是不完整的。由于对象的创建方法并不是原子的,因此在竞态条件下容易出错。在多线程环境下,会导致创建多个单例实例的后果。


     

    2.2 同步单例保证线程安全或者在类加载时初始化单例类实例

    要解决上面产生的竞态问题,需要在出现该问题的地方采用加锁机制。并且在实例返回后再释放锁。在java中使用synchronized关键字实现锁机制。

    package com.hzw.singleton;

    public class MySingleton{

      private static MySingleton instance;

      private MySingleton(){}

      public static synchronized MySingleton getInstance(){

        if(instance==null){

          instance = new MySingleton();

        }

        return instance;

      }

    }

    或者在加载类的同时创建单例实例,这样就不必同步单例实例的创建,并在JVM加载完所有类时就创建好单例对象(这是在类调用getInstance()方法之前发生的)。之所以可以这样,是因为静态成员与静态块是在类加载时执行的。

    package com.hzw.singleton;

    public class MySingleton{

      private static final MySingleton instance = new MySingleton();

      private MySingleton(){}

      public static MySingleton getInstance(){

        return instance;

      }

    }

    还可以使用静态块,不过这会导致延迟初始化,因为静态块是在构造方法调用之前执行的。

    package com.hzw.singleton;

    public class MySingleton{

      private static MySingleton instance = null;

      static{

        instance = new MySingleton();

      }

      private MySingleton(){}

      public static MySingleton getInstance(){

        return instance;

      }

    }


     

    2.3 使用双重检测锁

    双重检测锁是一种流行的创建单例的机制,它比其他方法更加安全,因为它会在锁定单例类之前检查一次单例的创建,在对象创建前再一次检查。

    package com.hzw.singleton;

    public class MySingleton{

      private volatile MySingleton instance;

      private MySingleton(){}

      public static MySingleton getInstance(){

        if(instance==null){

          synchronized(MySingleton.class){

            if(instance==null){

              instance = new MySingleton();

            }

          }

        }

        return instance;

      }

    }

    getInstance()方法会在创建前检查私有的MySingleton实例成员两次,看它是不是null,并赋值为一个MySingleton实例。


     

    2.4 使用枚举机制创建单例实例

    以上方法都不是绝对安全的。比如说,开发者可以通过Java Reflection API将构造方法的访问权限改为public,这样就可以再次创建单例了。

    在java中,创建单例的最佳方式是使用java5中引入的枚举类型,这也是Effective Java中极力推荐的方式。因为枚举类型本质上就是单例的,因此JVM会处理创建单例所需的大部分工作。这样通过使用枚举类型,就不需要在处理同步对象创建(单例实例的创建)和提供(获取创建的单例实例)等工作了,还能避免与初始化相关的问题。

    package com.hzw.singleton;

    public enum MySingletonEnum{

      INSTANCE;

      public void method1(){}

      public void method2(){}

      ....................

    }

    在该例中,对单例对象实例的引用是通过如下方式获得的:

    MySingletonEnum mse = MySingletonEnum.INSTANCE;

    一旦拥有了单例的引用,就可以调用它的方法:例如   mse.method1();

     

  • 相关阅读:
    7、Nginx基础Http原理
    8、nginx基础
    9、nginx常用基础模块
    第一章·MySQL介绍及安装
    第二章· MySQL体系结构管理
    第三章·MySQL版本区别及管理
    第四章· MySQL客户端工具及SQL讲解
    第五章· MySQL数据类型
    第六章· MySQL索引管理及执行计划
    第七章· MySQL的存储引擎
  • 原文地址:https://www.cnblogs.com/zwbg/p/6216677.html
Copyright © 2011-2022 走看看