详解Java设计模式之《单例设计模式》

2017-10-05  本文已影响0人  Michaelhbjian

单例设计模式(Singleton Pattern)是Java开发人员了解设计模式的第一种,也是最容易理解的,在平时的工作使用的很频繁的设计模式之一!

概念

单例设计模式(Singleton Pattern) :确保每一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法,属于创建型模式

作用

单例设计模式主要是为了避免因为创建多个实例造成的资源浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例设计模式能够保证整个应用中有且只有一个实例

实现方式

饿汉式

优点:在类加载 的时候就完成了实例化,避免了多线程同步问题。

缺点:由于在类加载的时候就实例化了,所以没有达到Lazy Loading(懒加载)的效果,也就是说我没有用到这个实例,但是它也会加载,会造成内存的浪费(但是这个浪费可以忽略的)

publice class Singleton{
  //在类加载的时候就创建一个对象
  private static Singleton instance = new Singleton();
  //初始化构造器
  private Singleton(){}
  //获取单例的对象
  public static Singleton getInstance(){
      return instance;
  }  
}

懒汉式

基础版

优点:

缺点:当多线程工作的时候,如果有多个线程同时运行到if(instance == null)都判断为空,那么这两个线程各自都会创建一个实例,这样就不是单例了。

public class Singleton{
  //先定义对象的引用
  private static Singleton instance;
  //用作初始化的构造器,定义为私有,防止外部的类调用
  private static Singleton(){};
  public static Singleton getInstance(){
      //如果多个线程在此处,则每个线程都会创建一个实例
      if(instance == null){
          instance =new Singleton();
      }
    return instance;
  }
}

Synchronized版本

优点:加上了synchronized关键字后,getInstance()方法就会锁上了。如果有两个线程同时执行到这个方法是,会有其中一个线程获得同步锁,进而继续执行,而另外一个线程需要等待,当T1线程执行完毕之后,T2才会执行。

缺点:加上了同步锁之后,会强制除T1以外的所有线程等待,会严重影响程序的执行效率

双重检查版本(Double-Check)

优点:在同步锁的外面在做一次判断,如果这个线程的实例没有被创建过,则放入;如果被创建过,则直接返回它的实例。

缺点:使用volatile 关键字会屏蔽Java虚拟机所做的一些代码优化,可能导致系统运行效率低。

注意:如果使用双重检查锁来实现懒汉式单例类,需要在静态成员变量实例之前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能够正确处理。

public class Singleton{
  //添加volatile 关键字
  private static volatile  Singleton instance;
  private static Singleton();
  private static Singleton getInstance(){
     //第一次判断:该线程的实例是否被创建
     if(instance == null){
        //使用同步代码块,由于此方法是静态的,所以同步锁是类名.class
        synchronized(Singleton.class){
           //第二次判断:为了防止了可能出现的多个实例的情况
           if(instance == null){
               intstance = new Singleton();
            }
        }
     }
     return instance; 
  }
}

静态内部类

优点:饿汉式单例类不是实现延时加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控制繁琐,而且性能受影响。我们在单例类中增加一个**静态内部类 **,在该内部类中创建单例对象,再将该单例对象通过getinstance方法返回给外部使用。

由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类SingletonHolder,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。

缺点:与编程语言本身的特性相关,很多面向对象语言不支持IoDH

public class Singleton{
  //静态内部类
  private static class SingletonHolder{
     private static final Singleton instance = new Singleton();  
  }
  private Singleton(){};
  //内部类是在需要被实例化时,调用getInstance()方法时,才会去装载SingletonHolder类
  public static final Singleton getInstance(){
      return SingletonHolder.instance;
  }
}

枚举

优点:它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次序列化。

缺点:不能通过reflection attack来调用构造方法。

public enum SingletonEnum{
  //枚举类型变量
  instance;
  public SingletonEnum(){
  }
  public void whateverMethod(){
  }
}

Java中的单例模式

Java的Runtime对象

在Java语言内部,java.lang.Runtime对象就是一个使用单例模式的例子。在每一个Java程序里面,都是唯一的一个Runtime对象,应用程序可以与其运行环境发生相互作用。

Runtime类提供了一个静态工厂方法getRuntime():

public static Runtime getRuntime();

通过调用此方法,可以获得Runtime类唯一的一个实例:

Runtime rt = Runtime.getInstance();

单例模式的优缺点

优点:

缺点:

应用场景

在一下情况下可以考虑使用单例模式:

参考资料

嘟嘟独立博客

刘伟技术博客

上一篇 下一篇

猜你喜欢

热点阅读