单例模式详解-2

声明:此处代码都是引用私塾Java设计模式
1.在介绍单例模式的另外一种实现方式之前,先介绍一下用Java实现缓存的简单方式
/**
 * Java中缓存的基本实现示例
 */
public class JavaCache {
	/**
	 * 缓存数据的容器,定义成Map是方便访问,直接根据Key就可以获取Value了
	 * key选用String是为了简单,方便演示
	 */
	private Map<String,Object> map = new HashMap<String,Object>();
	/**
	 * 从缓存中获取值
	 * @param key 设置时候的key值
	 * @return key对应的Value值
	 */
	public Object getValue(String key){
		//先从缓存里面取值
		Object obj = map.get(key);
		//判断缓存里面是否有值
		if(obj == null){
			//如果没有,那么就去获取相应的数据,比如读取数据库或者文件
			//这里只是演示,所以直接写个假的值
			obj = key+",value";
			//把获取的值设置回到缓存里面
			map.put(key, obj);
		}
		//如果有值了,就直接返回使用
		return obj;
	}
	
	//1:定义一个存放缓存数据的容器
	
	//2:从缓存中获取数据的做法
	
	//2.1:先从缓存里面取值
	//2.2:判断缓存里面是否有值
	//2.3:如果有值了,就直接使用这个值
	//2.4:如果没有,那么就去获取相应的数据,或者是创建相应的对象
	//2.4.1:把获取的值设置回到缓存里面
	
	//web开发 Scope===〉就是数据的缓存范围
	//<jsp:useBean name="aa" class="cn.javass.AModel" scope="request">
//	Object obj = request.getAttribute("aa");
//	AModel am = null;
//	if(obj==null){
//		am = new AModel();
//		request.setAttribute("aa",am);
//	}else{
//		am = (AModel)obj
//	}
	
	
}
 使用map容器来实现缓存,用空间来换取时间,在数据库中常常用到。
2.利用缓存来实现单例模式
/**
 * 使用缓存来模拟实现单例
 */
public class Singleton {
	/**
	 * 定义一个缺省的key值,用来标识在缓存中的存放
	 */
	private final static String DEFAULT_KEY = "One";
	/**
	 * 缓存实例的容器
	 */
	private static Map<String,Singleton> map = new HashMap<String,Singleton>();
	/**
	 * 私有化构造方法
	 */
	private Singleton(){
		//
	}
	public static Singleton getInstance(){
		//先从缓存中获取
		Singleton instance = (Singleton)map.get(DEFAULT_KEY);
		//如果没有,就新建一个,然后设置回缓存中
		if(instance==null){
			instance = new Singleton();
			map.put(DEFAULT_KEY, instance);
		}
		//如果有就直接使用
		return instance;
	}
	
	
}
 
3.现在来介绍懒汉式的线程不安全


 依照上图可以看出懒汉式是线程不安全的,因为在线程A即将要创建实例的时候,线程B也将创建实例,这样就创建了连个实例,所以线程不安全
 
4.接下来将解决懒汉式的线程不安全问题
  • 在全局访问点方法前加锁,即synchronized,这样即解决了线程不安全问题
  • 第一种做法是可取的,但是不是最好的,下面介绍双重检查加锁来解决线程不安全问题 

双重检查加锁指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块之后再次检查实例是否存在,如果不存在,就在同步的情况下创建实例,这是第二重检查,这样一来就只需要同步一次了,从而减少了多次在同步情况下进行判断浪费的时间

实现如下:

public class Singleton {
	/**
	 * 对保存实例的变量添加volatile的修饰
	 */
	private volatile static Singleton instance = null;
	private Singleton(){
		
	}
	public static  Singleton getInstance(){
		//先检查实例是否存在,如果不存在才进入下面的同步块
		if(instance == null){
			//同步块,线程安全的创建实例
			synchronized(Singleton.class){
				//再次检查实例是否存在,如果不存在才真的创建实例
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

   双重检查加锁机制的实现会使用一个关键字volatile,被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而使多个线程能正确处理该变量。

猜你喜欢

转载自tryhl.iteye.com/blog/2000146
今日推荐