单点模式:深潜


单例设计模式主要用于控制多个对象的创建,因此属于创建模式家族。

早期的趋势是最多创建一个对象,但在某些情况下,我们需要固定数量的对象;这个模式正好可以帮助我们。通常,我们将构造函数标记为私有的,以确保外部世界根本无法创建对象,并提供一个简单地返回对象的静态方法。它只在事先没有创建对象的情况下创建对象。

随着时间的推移,人们意识到了singletons的这种普通实现存在的一些问题,并对其进行了改进以解决这些问题。请注意,单身并不是对的或错的;只要它们适合你的问题领域。

在这篇演讲中,我们将研究单例的各种实现。

让我们看一下类图:

单次使用

有几个地方最好使用singleton模式。例如:日志记录,缓存,负载平衡,配置,通信(IO或远程以避免性能差)和DB连接池。Java API中单例的一个例子是Runtime类。

单例实现

下面是一种使用eager loading机制实现单例的普通方法,它也是线程安全的。

public class MySingleton {
     private static final MySingleton mySingleton = new MySingleton();
     private MySingleton(){}     
     public static MySingleton getInstance(){
        return mySingleton;
     }
}


另一方面,下面是一个使用惰性加载机制实现单例的示例。在多线程应用程序的情况下,这将是一个糟糕的方法。

class MySingleton {
     private static MySingleton mySingleton;
     private MySingleton(){}
     public static MySingleton getInstance(){
           if(null == mySingleton) {
                 mySingleton = new MySingleton();
           }
           return mySingleton;
     }
}


多线程方法可以避免竞争条件,以确保它不会违反单线程的哲学。但是在下面的示例中,使整个方法“同步”并不是一个好方法,因为我们只需要将锁放在对象创建语句上。

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


下面的多线程实现方式可以避免竞争条件,确保它不会违反单例的原理,并且在双重检查锁定的帮助下,使用对象级锁可以达到同样的目的。这个实现保证了线程的安全性;但是需要额外的对象来保持一个锁,这不是一个好的做法。另一个缺点是有人可以利用使用类级锁的优势,因为您的锁在一个不同的对象上。

class MySingleton {
  private static MySingleton mySingleton;
  private static final Object lock = new Object();
  private MySingleton(){}
  public static MySingleton getInstance(){
     if(null == mySingleton) {
         synchronized(lock) {
            if(null == mySingleton) {
               mySingleton = new MySingleton();
            }
         }   
     }
     return mySingleton;
  }
}


另一种基于多线程的实现(以避免竞争条件)可以在使用类级锁的双重检查锁定的帮助下实现。在这里,将MySingleton对象标记为volatile将确保一个线程所做的更改应该在另一个线程中可见。这个实现保证了线程的安全性。

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


这种实现方式提供了一个智能构造函数,该构造函数将使用反射停止单例契约违反。

class MySingleton {
    private volatile static MySingleton mySingleton;

    //Reflection can't hack to create more than one object.
    private MySingleton() throws Exception {
        if (null == mySingleton) {
            mySingleton = new MySingleton();
        } else {
            throw new Exception("It's a singleton class; don't expect more object to get produced");
        }
    }

    public static MySingleton getInstance() throws Exception {
        if (null == mySingleton) {
            synchronized(MySingleton.class) {
                if (null == mySingleton) {
                    mySingleton = new MySingleton();
                }
            }
        }
        return mySingleton;
    }
}


下面是一个非常流行的使用静态类的实现,它带来了延迟加载和线程安全的强大功能。

public class MySingleton {
    private MySingleton() {}
    private static class SingletonUisngInner {
        private static MySingleton mySingleton = new MySingleton();
    }

    public static MySingleton getInstance() {
        return SingletonUisngInner.mySingleton;
    }
}


在某些情况下,如果您的singleton类继承了可克隆的接口属性,那么您的singleton类需要格外小心以防止singleton设计契约。您的singleton类应该重写clone方法并显式抛出CloneNotSupportedException。

class ClonedClass implements Cloneable {
    //Some logic
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class MySingleton extends ClonedClass {
    private MySingleton() {}
    private static class SingletonUisngInner {
        private static MySingleton mySingleton = new MySingleton();
    }

    public static OneMore getInstance() {
        return singletonUisngInner.mySingleton;
    }

    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}


另一种,也是我们最后一种非常流行和聪明的实现单例的方法是使用enum,它解决了目前为止的所有问题。

public enum  EnumSingleton{
    INSTANCE;
}


有时,人们谈论跨多个JVM的单例,所以让我们来讨论一下。Singleton意味着只有一个对象,并且我们非常清楚对象生命周期是由JVM管理的,因此跨多个JVM共享一个对象是不可能的。

但是,如果需要,您可能可以在一个JVM中创建对象,并将其作为序列化对象分发,以便其他JVM使用(但是请记住,您正在反序列化它,因此请记住,任何静态的或标记为瞬态的东西都无法实现,而且在某个地方,单例协定正在脱离)。您还可以尝试使用RMI服务器对象作为单例来满足您的需要。

学习愉快!