• 第五式 单例模式


    单例模式

    什么是单例模式

      单例模式:确保一个类只有一个实例,并提供一个全局访问点。

      我们把某个类设计成自己管理的一个单独实例,同时也避免其他类再自行产生实例。同时提供该实例的全局访问点,当你需要实例时,向类查询,会返回单个实例。

    如何实现

      平时我们需要对象时,都是new一个出来。这次单例设计模式,通过new的方式,每次出来的对象都不是同一个了,不符合单例模式设计原则。new对象是通过

    类自身的公开的构造方法,我们可以通过将构造方式私有不让别人调用,然后提供一个静态方法让需要的人来获取该实例对象。根据该实例对象创建时间,可以分为

    懒汉式和饿汉式。

      饿汉式:

    1 //饿汉式
    2 public class Single {
    3     private static Single single = new Single();
    4     private Single(){}
    5     public static Single getInstance(){
    6         return single;
    7     }
    8 }

      懒汉式:

     1 //懒汉式
     2 public class Single {
     3     private static Single single;
     4     private Single(){}
     5     public static Single getInstance(){
     6         if(single == null){
     7             single = new Single();
     8         }
     9         return single;
    10     }
    11 }

      利弊:饿汉式在程序运行时就创建好了单例对象,这个对象可能会一直用不上,但一开始就被创建了占用资源,造成浪费。懒汉式是有人要用到这个单例对象时才把它创建出来,达到了

    资源利用的最合理化,但是他不是线程安全的。看下面的代码就知道了,私有的构造方法被调用了两次,也就是说没有实现输出单例。

     1 public class Client {
     2     public static void main(String[] args){
     3         for(int i=0;i<2;i++){
     4             //创建两个线程,去获取单例
     5             new Thread(){
     6                 public void run(){
     7                     Singleton.getInstance();
     8                 }
     9             }.start();
    10         }
    11     }
    12 }
    13 
    14 class Singleton {
    15     private static Singleton singleton;
    16     private Singleton(){
    17         System.out.println("我被创建了!");
    18         try {
    19             //模拟复杂对象创建时,耗费的时间
    20             Thread.sleep(1000);
    21         } catch (InterruptedException e) {
    22             e.printStackTrace();
    23         }
    24     }
    25     public static Singleton getInstance(){
    26         if(singleton == null){
    27             singleton = new Singleton();
    28         }
    29         return singleton;
    30     }
    31 }
    32 
    33 运行结果:
    34 我被创建了!
    35 我被创建了!

      如果我们将getInstance方法变成同步方法,就可以避免多线程问题。像下面这样

    1     public static synchronized Singleton getInstance(){
    2         if(singleton == null){
    3             singleton = new Singleton();
    4         }
    5         return singleton;
    6     }
    7 
    8  运行结果:
    9  我被创建了!

      通过增加synchronized关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要先等别的线程离开该方法,这样就没有两个线程同时进入这个方法了。虽然同步解决了线程安全这个问题,但是同步会降低性能,这又是另外一个问题。实际上只有第一次执行getInstance()方法时,才真正需要同步,一旦实例被创建完毕之后,就不需要同步了。优化一下代码

     1 public static Singleton getInstance(){
     2         if(singleton == null){
     3             synchronized (Singleton.class){
     4                 if(singleton == null){
     5                     singleton = new Singleton();
     6                 }
     7             }
     8         }
     9         return singleton;
    10     }
    11 
    12 运行结果:
    13 我被创建了!

      这方法叫做“双重检查加锁”,在getInstance()中减少使用同步。

      静态内部类实现单例模式,可以兼顾线程安全和懒加载。

     1 //这种方式,线程安全,调用效率高,并且实现了延迟加载
     2 class Singleton {
     3     private static class SingletonClassInstance{
     4         private static final Singleton singleton = new Singleton();
     5     }
     6     private Singleton(){
     7         System.out.println("我被创建了!");
     8         try {
     9             //模拟复杂对象创建时,耗费的时间
    10             Thread.sleep(1000);
    11         } catch (InterruptedException e) {
    12             e.printStackTrace();
    13         }
    14     }
    15     public static Singleton getInstance(){
    16         return SingletonClassInstance.singleton;
    17     }
    18 }
       运行结果:
      我被创建了!

       要点:外部类没有static属性,不会像饿汉式那样立即加载对象;只有调用getInstance才会加载静态内部类,而加载类是天然的线程安全的,singleton是static final类型,保证了内存中只有一个实例存在;兼备了并发高效调用和延迟加载的优势。

    应用场景

      1、Windows的Task Manager(任务管理器)就是典型的单例模式

      2、网站的计数器也是单例,不然不好同步

      3、应用程序的日志应用,一般都使用单例模式,因为共享的日志文件一直处于打卡状态,只能一个实例去操作,否则不好追加

      4、Spring中,每个Bean默认就是单例,这样可以方便被Spring容器管理

      5、spring MVC框架中,控制器对象也是单例

  • 相关阅读:
    python的配置
    SSI服务端包含技术
    IDEA使用过程中常见小问题
    IDEA配置maven,jdk,编码
    不使用SwitchHosts修改C:WindowsSystem32driversetchosts文件
    webstorm打开一个门户工程流程
    安装nginx流程
    webstorm配置node.js
    Linux的inode与block
    使用vsftpd 搭建ftp服务
  • 原文地址:https://www.cnblogs.com/bwyhhx2018/p/10753247.html
Copyright © 2020-2023  润新知