Javaのデザインパターンの研究ノート(5)シングルトン

序文

Benpianは、デザインパターンの研究は、このような他のモードに興味のような物品の一つを指摘し、あなたがそのアドレスから見つけることができているデザインパターンの研究ノートのサマリーアドレス

1.シングルトンを使用する理由

Windowsのタスクマネージャには、例えば、Windowsでは、タスクマネージャは、タスクマネージャを開くためにユニークな、何度も、常に唯一のポップアップタスクマネージャです。

そうする2つの理由があります。

  1. 資源節約
  2. データの矛盾の複数のインスタンスを避けます

1.1リソースを節約

あなたが複数のウィンドウをポップアップ表示することができ、そしてこれらのウィンドウの内容がまったく同じであれば、すべての重複オブジェクト、システムリソースを無駄にバインドされ、タスク管理が実行されているシステムに関する多くの情報を取得する必要があり、そのような情報へのアクセスは、いくつかのシステムリソースを消費する必要があり、資源は、廃棄物は恥ずべきである、とまったく同じ内容の複数ウィンドウ表示が不要となり、CPUとメモリのリソースが含まれます

1.2データの矛盾の複数のインスタンスを避けてください

複数のポップアップウィンドウ矛盾している場合、問題は、このようなウィンドウは、「CPU使用率」などを表示し、タスクマネージャなど、特定のインスタント情報システムリソースの使用状況やプロセスで複数の状態、および他のサービスが、ある意味、より深刻です真であるか最終的に15%、10%、ウィンドウBが表示され、「CPU使用率」、?これは純粋に、より望ましいユーザーに誤解を与える「いたずら」ユーザー、です。

オブジェクトの動機の一意性を確保するために、我々はSingletonパターンによって行うことができ、これはどこシングルトンパターンです

シングルトンの2.概要

Windowsのタスクマネージャをシミュレートすることで、シングルトンパターンを理解するためにタスクマネージャクラスを作成します

2.1タスクマネージャ

      /**
       * @author liuboren
       * @Title: 任务管理器类
       * @Description:
       * @date 2019/7/16 15:17
       */
      public class TaskManager {
          //初始化窗口
          public TaskManager() {
      
          }
      
          //显示进程
          public void displayProcesses() {
      
          }
      
          //显示服务
          public void displayServices() {
      
          }
      
      }
      

2.2タスクマネージャの再構築

Windowsのタスクマネージャの一意性を実現するために、我々は、以下の3つの手順でクラスを再構築する必要があります。

  • コンストラクタ民間修正
  • タイプのプライベートメンバ変数を定義してタスクマネージャ
  • 総静的メソッドを大きくすると、タスクマネージャをインスタンス化します

2.2.1コンストラクタ民間修正

一意タスクマネージャのインスタンスを確保するために、新しいオブジェクトのタスクマネージャクラスをインスタンス化するために、新しいキーワードを使用している場合、それぞれが持つことになりますので、我々は直接オブジェクトを作成するために、新しい外部クラスの使用を禁止する必要があるので、あなたは、タスクマネージャのコンストラクタを見てする必要があります代わりに、民間の、次のコードに示すように:

          private TaskManager() {……}

2.2.2プライベートメンバ変数の種類のタスクマネージャの定義

どのようにコンストラクタは、オブジェクトを作成し、民間の修正後に変更されるのですか?クラス外のオブジェクトを作成することが可能な、もはや新しいものであるが、それでもタスクマネージャの中に作成することができますが、視認性が外クラスに対してのみ有効ではありません、心配しないでください。したがって、我々は、タスクマネージャでのみインスタンスを作成し、保存することができます。国民はこのユニークなインスタンスへのアクセスを持ってもらうために、あなたは、コードは、以下に示すプライベートメンバ変数の型では、静的なタスクマネージャタスクマネージャを定義する必要があります。

          private static TaskManager tm = null;

2.2.3合計静的メソッドは、タスクマネージャをインスタンス増加

カプセル化メンバ変数を確保するために、我々は可視性TMタスクマネージャオブジェクトタイプがプライベートで設定されますが、外の世界になるというメンバ変数を使用するときのメンバ変数をインスタンス化する方法?答えは、パブリック静的メソッドを追加することで
、次のコードに示すように:

          public static TaskManager getInstance()
          {
          if (tm == null)
          {
          tm = new TaskManager();
          }
          return tm;
          }

2.2.4コード

この方法は、再び呼び出すときに作成された最初の返したときに一意のインスタンスを作成します)クラスの外で、私たちは直接、新しいタスクマネージャオブジェクトを作成することはできませんが、オブジェクトのインスタンスは、コードTaskManager.getInstance()、最初のコールのgetInstance(を介してアクセスすることができます例えば、オブジェクトの一意のインスタンスを確保するために

          
          /**
           * @author liuboren
           * @Title: 单例版任务管理器类
           * @Description:
           * @date 2019/7/16 15:24
           */
          public class TaskManagerSingleton {
              private static TaskManagerSingleton taskManagerSingleton = null;
              
              //初始化窗口
              private TaskManagerSingleton() {
              }
          
              public static TaskManagerSingleton getInstance(){
                  if (taskManagerSingleton == null){
                      taskManagerSingleton = new TaskManagerSingleton();
                  }
                  return taskManagerSingleton;
              }
          
              //显示进程
              public void displayProcesses() {
          
              }
          
              //显示服务
              public void displayServices() {
          
              }
          }
          

2.3定義

シングルトン(Singletonパターン):特定のクラスのインスタンスを1つだけ確保するため、システム全体のインスタンスをインスタンス化し、このクラスはグローバルアクセス方法を提供するシングルトンクラスと呼ばれている提供します。

Singletonパターンは、スキーマを作成することが目的です。

2.4三点

1.クラスのインスタンスは1つだけ。
2.それは自分自身のインスタンスを作成する必要があります。
3.それは、システム全体に自分の例を提供しなければなりません。

2.5構造図。

2.6役割

シングルトン(シングルトン):単一の実施形態において実装は、唯一のクラスは、内部のインスタンスを生成し、顧客は単なる例にアクセスできるように、それは、静的のgetInstance()ファクトリメソッドを提供し、外部のインスタンスを、それらを防止するために、そのプライベートコンストラクタのために設計された、単一の実施形態シングルトン、外部共有の例のみのクラスタイプ内の静的オブジェクト。

3.飢え式シングルトンと怠惰式シングルトン

   public static TaskManagerSingleton getInstance(){
          if (taskManagerSingleton == null){
              taskManagerSingleton = new TaskManagerSingleton();
          }
          return taskManagerSingleton;
      }

2インスタンス化されたオブジェクトをもたらす、高度に並行環境では、上記のコードはスレッドセーフされていないと同時に、2つのスレッドがあれば(taskManagerSingleton == null)を決定することによって存在することができます。

この問題を解決するために、二つの方法は1つがあるがある怠惰な人間のシングルトンクラス。もう一つの例は、飢えた男単一のクラスです

注:同時実行の知識を持っていなかった場合、あなたは私の以前のブログを参照することができます並行プログラミングの勉強はシリーズノート

3.1式のシングルトンを飢え

      /**
       * @author liuboren
       * @Title: 饿汉式单例
       * @Description:
       * @date 2019/7/16 16:30
       */
      public class EagerSingleton {
          private static final EagerSingleton instance = new EagerSingleton();
          private EagerSingleton() { }
          public static EagerSingleton getInstance() {
              return instance;
          }
      }

クラスがロードされると、静的インスタンス変数が初期化され、その後、プライベートクラスのコンストラクタが呼び出され、シングルトンクラスの唯一のインスタンスが作成されます。単一のクラスを飢え例は、シングルトンオブジェクトの一意性を保証します。

3.2怠惰式シングルトン

      /**
       * @author liuboren
       * @Title: 懒汉式单例类
       * @Description:
       * @date 2019/7/16 16:41
       */
      public class LazySingleton {
          private static LazySingleton instance = null;
      
          private LazySingleton() { }
          
          synchronized public static LazySingleton getInstance() {
              if (instance == null) {
                  instance = new LazySingleton();
              }
              return instance;
          }
      }
      

式のシングルトンパフォーマンスの最適化を飢え3.2.1

複数のスレッドへの同時アクセスの問題に対処するために、スレッド・ロックに同期した表キーワードを増加させるの実施形態遅延単一クラスのgetInstance()メソッド。あなたは、スレッドロック決意しかし、上記のコードは、セキュリティスレッドを解決するが、それぞれのgetInstance()を呼び出し、マルチスレッドの高い同時アクセス環境、それは、システムのパフォーマンス低下の原因になりますする必要があります。どのように両方のスレッド安全性の問題を解決するために、システムのパフォーマンスに影響を与えませんか?私たちは、怠惰な、単一の例を改善し続けています。「インスタンス=新しいLazySingletonを();」実際には、我々は唯一コーディングする必要がロックする全体のgetInstance()メソッドを、必要としないロックをすることができます。次のようにこうしてのgetInstance()メソッドを修正することができます。

          
          public static LazySingleton getInstance() {
          
          if (instance == null) {
          
          synchronized (LazySingleton.class) {
          
          instance = new LazySingleton();
          
          }
          
          }
          
          return instance;
          
          }
          

式のシングルトンを飢え3.2.2ダブルチェックロック

問題が解決されたように見える、そうではありません。単一の実施形態に上記のコード場合、単一の静止物体が唯一の実施形態ではありませんがあります。

原因如下:假如在某一瞬间线程A和线程B都在调用getInstance()方法,此时instance对象为null值,均能通过instance == null的判断。由于实现了synchronized加锁机制,线程A进入synchronized锁定的代码中执行实例创建代码,线程B处于排队等待状态,必须等待线程A执行完毕后才可以进入synchronized锁定代码。但当A执行完毕时,线程B并不知道实例已经创建,将继续创建新的实例,导致产生多个单例对象,违背单例模式的设计思想,因此需要进行进一步改进,在synchronized中再进行一次(instance == null)判断,这种方式称为双重检查锁定(Double-Check Locking)。使用双重检查锁定实现的懒汉式单例类完整代码如下所示:

          
          /**
           * @author liuboren
           * @Title: 完美版 懒加载单例类
           * @Description:
           * @date 2019/7/16 16:47
           */
          public class LazySingletonPerfect {
              private volatile static LazySingletonPerfect instance = null;
          
              private LazySingletonPerfect() {
              }
          
              public static LazySingletonPerfect getInstance() {
                      //第一重判断
                  if (instance == null) {
                      //锁定代码块
                      synchronized (LazySingleton.class) {
                      //第二重判断
                          if (instance == null) {
                              instance = new LazySingletonPerfect(); //创建单例实例
                          }
                      }
                  }
                  return instance;
              }
          
          }
          

3.3 饿汉式单例类与懒汉式单例类比较

3.3.1 饿汉式

优点:

  1. 线程安全: 饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性

  2. 调用速度优于懒汉式: 从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例

缺点: 资源利用效率不如懒汉式且加载时间较长无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长

3.3.2 懒汉式

优点:

  1. 延迟加载: 懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载

缺点:

  1. 非线程安全: 多线程访问懒汉式单例类可能会创建多个实例

  2. 性能稍差通过双重检查锁定等机制保证线程安全,这将导致系统性能受到一定影响

4.更好的单例实现方法

饿汉式单例类不能实现延迟加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控制烦琐,而且性能受影响。

可见,无论是饿汉式单例还是懒汉式单例都存在这样那样的问题,有没有一种方法,能够将两种单例的缺点都克服,而将两者的优点合二为一呢?答案是:Yes!下面我们来学习这种更好的被称之为Initialization Demand Holder (IoDH)的技术

  /**
   * @author liuboren
   * @Title: IoDh技术
   * @Description: Java最好的单例实现模式
   * @date 2019/7/17 10:25
   */
  public class IoDHSingleton {
  
      private IoDHSingleton() {
      }
  
      //静态内部类的成员变量才能是静态的
      private static class HolderClas{
  
          public static IoDHSingleton ioDHSingleton = new IoDHSingleton();
      }
  
      public static  IoDHSingleton getInstance(){
          return HolderClas.ioDHSingleton;
      }
  
      public static void main(String[] args) {
          IoDHSingleton s1,s2;
          s1 = IoDHSingleton.getInstance();
          s2 = IoDHSingleton.getInstance();
          System.out.println(s1 == s2);
  
      }
  }
  

编译并运行上述代码,运行结果为:true,即创建的单例对象s1和s2为同一对象。由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。

通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式(其缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH)

5. 总结

单例模式作为一种目标明确、结构简单、理解容易的设计模式,在软件开发中使用频率相当高,在很多应用软件和框架中都得以广泛应用

5.1 优点

(1) 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。

(2) 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。

(3) 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例单例对象共享过多有损性能的问题。

5.2 缺点

(1) 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

(2) 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。

(3) 现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失

5.3 适用场景

(1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。

(2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

おすすめ

転載: www.cnblogs.com/xisuo/p/11204952.html