`

设计模式——单例模式

 
阅读更多

 

 

 

1 初识单例模式:
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点

结构:

 

参考实现:

 

public class Singleton {
 //饿汉式 线程安全下 双重检查机制 两个判null 一个synchronized ;
 //好处在于 线程安全 并 减少多次同步下进行判断所浪费的时间
	/**
	 * 对保存实例的变量添加volatile的修饰 这样当一个线程修改后 别的线程能立马感知到
    线程之间共享 volatile这个内存空间  并只有在jdk1.5之后才能使用	 
*/
	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;
	}
}

 

 

 

2 体会单例模式:

 

场景问题:  读取配置文件的内容(配置文件内容是固定的,在系统中,得到配置文件的实例类仅仅有一个即可)

不用模式的解决方案: 每次都要new对象 并从配置文件中加载,  new出多少个对象就加载多少次;

 

 

使用模式的解决方案:  将类设计成静态类,那么对象就一个,加载仅仅加载一次配置文件

 

package cn.javass.dp.singleton.example4;

import java.io.*;
import java.util.*;
/**
 * 读取应用配置文件,单例实现
 */
public class AppConfig {
	/**
	 * 定义一个变量来存储创建好的类实例,直接在这里创建类实例,只会创建一次
	 */
	private static AppConfig instance = new AppConfig();
	/**
	 * 定义一个方法来为客户端提供AppConfig类的实例
	 * @return 一个AppConfig的实例
	 */
	public static AppConfig getInstance(){
		return instance;
	}
	
	/**
	 * 用来存放配置文件中参数A的值
	 */
	private String parameterA;
	/**
	 * 用来存放配置文件中参数B的值
	 */
	private String parameterB;
	
	public String getParameterA() {
		return parameterA;
	}
	public String getParameterB() {
		return parameterB;
	}
	/**
	 * 私有化构造方法
	 */
	private AppConfig(){
		//调用读取配置文件的方法
		readConfig();
	}
	/**
	 * 读取配置文件,把配置文件中的内容读出来设置到属性上
	 */
	private void readConfig(){
		System.out.println("开始咯");
		Properties p = new Properties(); 
		InputStream in = AppConfig.class.getResourceAsStream("AppConfig.properties");
		try {
			p.load(in);
			//把配置文件中的内容读出来设置到属性上
			this.parameterA = p.getProperty("paramA");
			this.parameterB = p.getProperty("paramB");
		} catch (IOException e) {
			System.out.println("装载配置文件出错了,具体堆栈信息如下:");
			e.printStackTrace();
		}
	}
	
}


client:
public class Client {
	public static void main(String[] args) {
		//创建读取应用配置的对象
		for(int i=0; i<3; i++){
			AppConfig config = AppConfig.getInstance();
			
			String paramA = config.getParameterA();
			String paramB = config.getParameterB();
			
			System.out.println("paramA="+paramA+",paramB="+paramB);
		}
		
	}
}

 

 

 

 

 

3 理解单例模式:

 

单例模式命名:  建议方法命名为: getInstance() 

单例模式:

懒汉式:以时间换取空间(每次都执行if判定,如果有实例,则不new 否则new)

饿汉式:以空间换取时间(直接new出对象来,占用空间)

延迟加载:最开始不加载,直到马上要使用到时才加载。

缓存思想:将经常用到的数据放在内存中,需要用的时候去内存获取(空间换时间)

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;
	}
}

 

利用缓存实现单例模式:

public class cacheSingleton {

	private static cacheSingleton instance;
	private cacheSingleton(){}
	
	public static  Map cacheMap = new HashMap();
	
	private static String key = "key1";
	
	public static cacheSingleton getInStance(){
		
		instance = (cacheSingleton) cacheMap.get(key);
		if(instance == null){
			instance = new cacheSingleton();
			cacheMap.put(key, instance);
		}
		return instance;
	}
	
}

 

单例模式优缺点:

    懒汉式在多线程下是线程不安全的

  

    但是饿汉式能够保证线程安全,JVM会保证只会装载一次

    懒汉式多线程安全,只需在getInstance(){}内new 实例时 增加synchronized即可。

    代码写法见文章最开头

 

 

java中一种更好的单例实现方式:(Lazy initialization holder class模式)

package cn.javass.dp.singleton.example11;

/**
 * 0 如下写法好处:
 * 1 实现延迟加载
 * 2 实现线程安全
 * @author zm
 *
 */
public class Singleton {
	/**
	 * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系,
	 * 而且只有被调用到才会装载,从而实现了延迟加载
	 */
	private static class SingletonHolder{
		/**
		 * 静态初始化器,由JVM来保证线程安全
		 */
		private static Singleton instance = new Singleton();
	}
	/**
	 * 私有化构造方法
	 */
	private Singleton(){
	}
	
	public static  Singleton getInstance(){
		return SingletonHolder.instance;
	}
}

 

 

 

单例和枚举: 单元素的枚举类型已经成为实现Singleton的最佳方法

 

/**
 * 使用枚举来实现单例模式的示例
 */
public enum Singleton {	
	/**
	 * 定义一个枚举的元素,它就代表了Singleton的一个实例
	 */
	uniqueInstance;
	
	/**
	 * 示意方法,单例可以有自己的操作
	 */
	public void singletonOperation(){
		//功能处理
	}
}

 

 

 

4 思考单例模式:

 

本质: 控制实例数目

 

何时选用:   大多应用在配置文件获取上吧

 

5 单例模式 扩展为3个写法:

package cn.javass.dp.singleton.example9;
import java.util.*;
/**
 * 简单演示如何扩展单例模式,控制实例数目为3个 
 * 
 * 1 使用map 
 * 2 使用单例
 * 3 设置单例个数,然后将每次生成的对象放在map中
 */
public class OneExtend {
	/**
	 * 定义一个缺省的key值的前缀
	 */
	private final static String DEFAULT_PREKEY = "Cache";
	/**
	 * 缓存实例的容器
	 */
	private static Map<String,OneExtend> map = new HashMap<String,OneExtend>();
	/**
	 * 用来记录当前正在使用第几个实例,到了控制的最大数目,就返回从1开始
	 */
	private static int num = 1;
	/**
	 * 定义控制实例的最大数目
	 */
	private final static int NUM_MAX = 3; 
	private OneExtend(){}
	public static OneExtend getInstance(){
		String key = DEFAULT_PREKEY+num;
		OneExtend oneExtend = map.get(key);
		if(oneExtend==null){
			oneExtend = new OneExtend();
			map.put(key, oneExtend);
		}
		//把当前实例的序号加1
		num++;
		if(num > NUM_MAX){
			//如果实例的序号已经达到最大数目了,那就重复从1开始获取
			num = 1;
		}
		return oneExtend;		
	}
	
	public static void main(String[] args) {
		OneExtend t1 = getInstance();
		OneExtend t2 = getInstance();
		OneExtend t3 = getInstance();
		OneExtend t4 = getInstance();
		OneExtend t5 = getInstance();
		OneExtend t6 = getInstance();
		
		System.out.println("t1=="+t1);
		System.out.println("t2=="+t2);
		System.out.println("t3=="+t3);
		System.out.println("t4=="+t4);
		System.out.println("t5=="+t5);
		System.out.println("t6=="+t6);
	}
}


 

 

 

6 单例模式脑图:

 

 

 

 

  • 大小: 16.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics