java  设计模式之单例模式

 更新时间:2017年02月19日 11:49:38   作者:ionetwogo  
这篇文章主要介绍了java 设计模式之单例模式的相关资料,需要的朋友可以参考下

java  设计模式之单例模式

前言:

在软件开发过程中常会有一些对象我们只需要一个,如:线程池(threadpool)、缓存(cache)、对话框、偏好设置等。这些对象如果制造出多个实例的话可能会导致一些不必要的麻烦,如:程序行为异常、资源使用过量等。这时单例模式就可以确保一个类只有一个实例,并提供全局访问点。下面是从简单的单例类来探讨该用何种方法实现单例模式。

/**
 * 最经典的单例类
 */
public class Singleton {
  // 设置成静态变量来记录Singleton的唯一实例
  private static Singleton singleInstance;
  private Singleton(){
    // 构造方法声明为私有的,这样只能在Singleton类中才能调用此构造方法
  }
  /*
   * 获取Singleton对象,如果还未实例化则实例化一个对象并返回这个实例
   */
  public static Singleton getInstance(){
    if (singleInstance == null) {
      singleInstance = new Singleton();
    }
    return singleInstance;
  }
  // 其他方法
}

从上面的例子可以看出Singleton类自己管理这个类的实例化过程,而且提供了全局访问点,就是设置成静态的getInstance()方法,在其他类要使用Singleton时它会返回一个实例。这中单例模式有个优点就是延迟实例化,简单地说延迟实例化就是延迟初始化,在类需要时才创建其实例,而不是在开始加载这个类时就创建出一个实例,这样的好处是可以避免性能的浪费。例如有些对象无需程序一开始就使用,或者其在程序执行的过程中就没有使用过。但是此例子却又一个缺点,那就是线程不够安全。因为如果有多个线程同时执行到getInstance()方法,而Singleton又还未new Singleton()一个实例,那么线程就会都认为singleInstance为null,就都会实例化Singleton,这时就会产生多个Singleton实例,明显不符合单例模式的初衷。那么接下来可能要做的就是对其进行改进

public class SingletonA {
  private static SingletonA singletongA;
  private SingletonA(){

  }
  /*
   * 增加synchronized关键字把getSingletonA方法变为同步方法
   */
  public static synchronized SingletonA getInstanceA(){
    if (singletongA == null) {
      singletongA = new SingletonA();
    }
    return singletongA;
  }
  // 其他方法
}

从这个例子上看增加了synchronized可以使getInstanceA()变成一个同步的方法,这时线程在进入这个方法之前就需要等待其他线程离开这个方法才能进入,也就使得该方法只能同时存在一个线程在执行它。

可能差不多问题解决了,但是要知道同步方法是会影响程序执行效率的,在此例子中我们只是为了解决第一个例子中第一次执行getInstance()方法不会产生多个实例,而这个例子中却会导致每次需要实例时都会调用getInstanceA()同步方法,而在已经有实例之后的调用synchronized就会是累赘,因为我们已经无需担心这个单例类会再次被创建出新的实例。因此我们还需要做一下改进。

既然上面说到延迟实例化,那么如果是不用的话那就简单多了。

public class SingletonB {
  // 在静态初始化器(static initializen)中创建单例,保证线程安全
  private static SingletonB singletonB = new SingletonB();
  private SingletonB(){
    // 构造函数
  }
  public static SingletonB getInstaceB(){
    // 已经实例化了,直接使用它
    return singletonB;
  }
}

上面的这种做法是在JVM加载这个类时马上创建一个实例,因为JVM会在线程访问这个实例之前就创建出该实例,因此线程是安全的。但这相较于延迟实例化而言可能会出现资源的浪费。而且如果此类较大的情况下会时程序初始化时间加长。

那么是否可以在使用用延迟实例化的同时又不会造成线程不安全且增加访问效率呢。接下来就用双重检查加锁来改进一下。

/**
 * 双重锁单例模式
 */
public class SingletonC {
  private volatile static SingletonC singletonC;
  private SingletonC(){

  }
  public static SingletonC getInstanceC(){
    if (singletonC == null) {
      synchronized (SingletonC.class) {
        if (singletonC == null) {
          singletonC = new SingletonC();
        }
      }
    }
    return singletonC;
  }
}

上面的例子是先检查实例,如果不存在则进入同步区块,进入同步区块之后再次检查,如果还是null才会创建实例,因而singletonC = new SingletonC()只会执行一次,而之后调用getInstanceC()时就因为有实例直接返回,所以除了第一次调用时会走同步,而之后便不会如第二个例子那样每次都会走同步方法。这样就可以使得执行getInstanceC()的时间减少。想必这里会发现有个volatile关键字,其作用是使得singletonC被初始化后对所有线程可见,多个线程可以正确地处理这个SingletonC变量。但要注意的:volatile关键字只能在Java 5及其之后使用,如果在此版本之前会导致这个双重检查失效。

在使用单例模式时,如果有多个类加载器(classloader)时需要自行指定类加载器,并指定用一个类加载器。因为每个类加载器都定义了一个命名空间,不同的类加载器可能会加载同一个类,从而导致单例类创建出多个实例。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

相关文章

  • java notify和notifyAll的对比

    java notify和notifyAll的对比

    这篇文章主要介绍了 java notify和notifyAll的对比的相关资料,需要的朋友可以参考下
    2017-02-02
  • Java如何通过jstack命令查询日志

    Java如何通过jstack命令查询日志

    在分析线上问题时常使用到jstack <PID>命令将当时Java应用程序的线程堆栈dump出来,面对jstack 日志,我们如何查看?下面小编给大家介绍下Java如何通过jstack命令查询日志,感兴趣的朋友一起看看吧
    2023-03-03
  • Java高并发下锁的优化详解

    Java高并发下锁的优化详解

    这篇文章主要介绍了Java高并发下锁的优化详解,锁是最常用的同步方法之一,在高并发的环境下,激烈的锁竞争会导致程序的性能下降,下面是一些关于锁的使用建议,可以把这种副作用降到最低,需要的朋友可以参考下
    2024-01-01
  • maven中更改jdk版本的方法实现

    maven中更改jdk版本的方法实现

    本文主要介绍了maven中更改jdk版本的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-07-07
  • 使用Spring Boot实现操作数据库的接口的过程

    使用Spring Boot实现操作数据库的接口的过程

    本文给大家分享使用Spring Boot实现操作数据库的接口的过程,包括springboot原理解析及实例代码详解,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • SpringBoot中忽略实体类中的某个属性不返回给前端的方法(示例详解)

    SpringBoot中忽略实体类中的某个属性不返回给前端的方法(示例详解)

    本文介绍了在Spring Boot中使用Jackson和Fastjson忽略实体类属性不返回给前端的方法,在Jackson中,同时使用@JsonProperty和@JsonIgnore时,@JsonIgnore可能失效,Fastjson中可以使用@JSONField(serialize=false)来实现,本文结合实例代码介绍的非常详细,需要的朋友参考下吧
    2024-11-11
  • Java实战之课程在线学习系统的实现

    Java实战之课程在线学习系统的实现

    本文将采用SpringBoot+Spring+Mybatis+Thyeleaf实现一个课程在线学习系统,采用SpringBoot框架实现 前台模板用的thymeleaf数据库层采用mybatis框架注解模式,感兴趣的可以了解一下
    2022-04-04
  • Java获取手机号码归属地的实现

    Java获取手机号码归属地的实现

    这篇文章主要介绍了Java获取手机号码归属地的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • JavaWeb如何实现禁用浏览器缓存

    JavaWeb如何实现禁用浏览器缓存

    这篇文章主要介绍了JavaWeb如何实现禁用浏览器缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 示例解析java面向对象编程封装与访问控制

    示例解析java面向对象编程封装与访问控制

    这篇文章主要为大家介绍了java封装与访问控制的示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05

最新评论