单例模式和双重检测的小结

设计模式的经典模式之一——单例模式

这个也是面试经常被问起的面试,一般都要求手写

【饿汉模式】非延时加载 ——以空间换时间

懒汉模式】延时加载——以时间换空间

看似简单,但是在懒汉模式还隐藏了一个双重检测,其中还是涉及到JVM内存的“无序写入”问题(后面使用的 volatile


1、饿汉模式

比较简单,非延时加载,一上来就把对象给new出来了

<span style="font-family:Microsoft YaHei;font-size:14px;">public class Singleton {
	private Singleton(){} //默认构造器
	private static Singleton instance = new Singleton();//直接加载
	public Singleton getIntance(){
		return instance;
	}
}</span>

2、懒汉模式,延时加载

【1】普通

<span style="font-family:Microsoft YaHei;font-size:14px;">public class Singleton {
	private Singleton() { } // 默认构造器

	private static Singleton instance = null;// 延时加载
	//每次运行都要创建实例因而需要加锁保护
	public static synchronized Singleton getIntance() {
		if (instance == null) {
			instance = new Singleton();// 判断之后加载
		}
		return instance;
	}

}</span>



【2】双重检测(有缺陷来自JVM自身)

上面尽管这样做到了线程安全,并且解决了多实例问题,但并不高效。在任何调用这个方法的时候,你都需要承受同步带来的性能开销,然而同步只在第一次调用的时候才被需要,也就是单例类实例创建的时候。这将促使我们使用双重检查锁模式(double checked locking pattern),一种只在临界区代码加锁的方法。一般称其为双重检查锁,因为会有两次检查 _instance == null,一次不加锁,另一次在同步块上加锁。

<span style="font-family:Microsoft YaHei;font-size:14px;">public class Singleton {
	private Singleton() { } // 默认构造器

	private volatile static Singleton instance = null;// volatile 可见性/防止重排序

	public static Singleton getIntance() {
		// 第一重判断
		if (instance == null) {
			// 锁定代码块
			synchronized (Singleton.class) {
				// 第二重判断
				if (instance == null) {
					instance = new Singleton();// 判断之后加载
				}

			}

		}
		return instance;
	}

}</span>
如果使用双重检查锁定来实现懒汉式单例类,需要在静态成员变量instance之前增加修饰符volatile(可见性,防止重排序),被volatile修饰的成员变量可以确保多个线程都能够正确处理,且该代码只能在JDK 1.5及以上版本中才能正确执行。

【3】ThreadLocal模式的单例

<span style="font-family:Microsoft YaHei;font-size:14px;">    public class Singleton{  
      public static final ThreadLocal  threadInstance=new ThreadLocal();  
     public static Singleton singleton=null;  
      public static getSingleton(){  
         if(threadInstance.get()==null){  
             createSingleton();  
          }  
      }  
      public static Singleton createSingleton(){  
         synchronized(Singleton.class){  
           if(singleton==null){  
                singleton=new Singleton();  
            }  
          }  
      }  
      
    }   </span>

ThreadLocal的全局变量局部化 第一次检测采用线程局部来做如果线程已经访问过则不需要进入同步块


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