HeadFirst 设计模式 - Chapte5 单例模式

Author Avatar
thychan Nov 10, 2016
  • Read this article on other devices

定义

确保单例对象的类只有一个实例,并提供一个全局访问点(全局资源只有一份)。
img

1
在java实现单例模式需要私有的构造器,一个静态方法和一个静态变量。

好处

这样有利于我们协调系统行为。比如在数据库连接或者线程池,或者在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取配置信息。这种方式简化了在复杂环境下的配置管理。

单线程下单例模式

代码 : 延迟方式 利用私有的构造器,只有单例类内部才可以调用构造器。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){
}
// 请求得到一个实例 : 延迟的方式
public static Singleton GetInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}

多线程下单例模式的问题

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁 (虽然这样会降低效率)

互斥锁会带来效率的下降(只有在第一次执行这个方法时候才需要同步),改善方法:

  1. 使用“急切eagerly”创建实例,而不用延迟化的方法。

    即 静态初始化器中创建单例。保证了线程安全: private static ChocolateBoiler uniqueInstance = new ChocolateBoiler();. JVM在加载这个类时马上创建此唯一的单例实例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class ChocolateBoiler {
    private boolean empty;
    private boolean boiled;
    // 静态初始化器中创建单例。保证了线程安全
    private static ChocolateBoiler uniqueInstance = new ChocolateBoiler();
    private ChocolateBoiler(){
    this.empty = true;
    this.boiled = false;
    }
    public void fill(){
    if(isEmpty()){
    empty = false;
    boiled = false;
    }
    }
    }
  2. double checked locking 双重检查加锁

    即临界区的代码仅仅需要加锁一次(创建的时候),同时档期获取锁的时候必须是线程安全的。可以用double checked locking来减少竞争和加锁的负担。

  • double checked locking是单例模式的多线程版本,如果是单线程可以使用延迟模式。

  • double checked locking 模式依就会使用锁——临界区锁定,不要以为可以避免使用锁。

  • double checked locking 模式解决的问题是:当多个线程存在访问临界区企图时,保证了临界区只需要访问一次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Singleton2 {
    private volatile static Singleton2 uniqueInstance;
    private Singleton2(){}
    public static Singleton2 getInstance(){
    if(uniqueInstance == null){
    synchronized (Singleton2.class) {
    if(uniqueInstance == null){
    uniqueInstance = new Singleton2();
    }
    }
    }
    return uniqueInstance;
    }
    }

注:该文章转载自:https://segmentfault.com/u/secondlife/articles