设计模式一:单例模式

使用一定的方法,保证在整个软件系统中,对某个类只能存在一个对象实例

单例模式的实现有多种方式,下面一一介绍。

1 饿汉式

所谓饿汉式,就是不管需不需要,都先new一个实例出来,而不是调用get方法的时候再new出来

public class Singleton1 {
  private static final Singleton1 instance = new Singleton1();
  private Singleton1(){}
  public static Singleton1 getInstance(){
    return instance;
  }
}

私有构造方法,无法使用new来创建对象,在类的内部创建一个实例,提供一个静态方法,每次都返回同一个实例。
当然,也可以通过静态代码块来实现:

public class Singleton2 {
  private static Singleton2 instance;
  static {
    instance = new Singleton2();
  }
  private Singleton2(){}
  public static Singleton2 getInstance(){
    return instance;
  }
}

总的来说其实没有什么区别,都是再类装载的时候完成实例化,借此避免了线程同步的问题。这种方法实现简单,但没有达到lazy loading的效果。如果从始至终从未使用过这个实例,会找出内存的浪费。如果不在意这点内存的浪费,使用这种方式完全没问题。

2 懒汉式

所谓懒汉式,就是上面说的懒加载了,在调用get方法的时候再初始化。

public class Singleton3 {
  private static Singleton3 instance;
  private Singleton3(){}
  public static Singleton3 getInstance(){
    if(instance == null){
      instance = new Singleton3();
    }
    return instance;
  }
}

这种方式虽然实现了懒加载,但在多线程的情况下,无法保证只new出一个对象。比如当线程1调用get方法,判断完instance为null,此时执行权转到线程2,线程2也判断到instance为null,因为此时线程1还没有创建实例,所以会出现new多个实例的情况。这种实现方式在多线程环境下是不安全的,不推荐使用。

如果想实现线程安全,也很简单,给get方法加锁:

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

如上,在get方法加锁,就可以保证线程安全了。但如果在方法上加锁,会降低get方法的效率,因为get方法一次只能有一个线程执行。其实在这个方法中,只需要在第一实例化的时候加锁就可以了,后续调用get方法不需要再加锁。如下:

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

这种实现方法也称为双重检查法。需要注意的是,instance成员变量要使用volatile关键字修饰,该关键字主要有如下两个作用:

保证不同线程对这个变量进行操作时的可见性,即一个线程修改了该变量的值,新值对其他线程来说是立即可见的。
禁止进行指令重排序。

3 静态内部类实现

public class Singleton6 {
  private Singleton6(){}

  public static Singleton6 getInstance(){
    return SingletonInstance.instance;
  }
  private static class SingletonInstance{
    private static final Singleton6 instance = new Singleton6();
  }
}

当Singleton6加载时,并不会加载SingletonInstance类的装载,只有调用getInstance方法时,SingletonInstance才会加载,借用类装载机制保证线程安全

4 枚举实现

public enum Singleton7 {
  INSTANCE;
}

枚举类默认将构造方法私有化,而INSTANCE就是Singleton7的一个实例。可以通过如下方法回去实例:

Singleton7.INSTANCE

使用枚举不仅能避免多线程问题,还能防止反序列化重新创建新的对象。

你可能感兴趣的:(设计模式)