简介
单例模式属于创建者模式。单例类提供了一种访问其内部唯一的对象的方法,可以直接访问,无需实例化该类的对象。
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
关键在于私有化构造函数
实现方式
1.线程不安全的懒汉式实现方式
懒汉式表示不在一开始就创建对象,在需要的时候再创建
public class LazyMode {
/**
* 私有化构造方法
*/
private LazyMode() {}
/**
* 静态私有的实例
* static保证类没有创建实例也可以用这个实例
*/
private static LazyMode INSTANCE = null;
/**
* public的访问单例的方法
* 这个方法也必须是static的,不然无法返回static的INSTANCE
*
* 注意,这个方法是线程不安全的
*/
public static LazyMode getInstance() {
if (INSTANCE == null){
INSTANCE = new LazyMode();
}
return INSTANCE;
}
}
2.线程安全的懒汉式实现方式
public class LazyModeSafe {
private LazyModeSafe() {}
private static LazyModeSafe INSTANCE;
public static synchronized LazyModeSafe getInstance() {
if (INSTANCE == null){
INSTANCE = new LazyModeSafe();
}
return INSTANCE;
}
}
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
3.饿汉式
基于 classloader 机制避免了多线程的同步问题,但是没有做到lazy load
public class HungryMode {
private HungryMode() {}
private static HungryMode INSTANCE = new HungryMode();
public static HungryMode getInstance() {
return INSTANCE;
}
}
4.双检锁/双重校验锁(DCL,即 double-checked locking)
public class DoubleCheckMode {
private static volatile DoubleCheckMode instance;
private DoubleCheckMode (){
}
public static DoubleCheckMode getInstance() {
if (instance == null) {
synchronized (DoubleCheckMode.class) {
if (instance == null) {
instance = new DoubleCheckMode();
}
}
}
return instance;
}
}
这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例
DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用静态内部类单例模式来替代DCL。
5.静态内部类实现单例模式
public class StaticInternClass {
private StaticInternClass() {}
private static class LazyHandler {
private static final StaticInternClass INSTANCE = new StaticInternClass();
}
public static StaticInternClass getInstance() {
return LazyHandler.INSTANCE;
}
}
使用静态内部类持有单例对象
第一次加载SingletonIntern类时并不会初始化INSTANCE
只有在第一次调用getInstance方法时虚拟机加载LazyHandler并初始化INSTANCE
这样不仅利用类加载机制保证了线程安全,还可以实现Lazy Load
6.枚举模式
public enum EnumMode {
INSTANCE;
public void doSomeThing() {
}
}
枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。
总结
使用饿汉式、双检锁、静态内部类这三种模式