设计模式学习之单例模式

 

一、单例模式定义:

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口

二、单例模式特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

三,单例模式

Spring中默认配置的bean的scope为singleton,也就是单例作用域以及最早使用的MVC框架Struts1中的action就是单例模式的。单例模式应用的框架。

四, 单例模式的六种实现方式

1.饿汉式单例(立即加载方式)

/**
 * 单例模式 
 * 饿汉式
 * 直接创建实例对象 不管你是否需要该实例对象
 * 不存在线程安全的问题
 *
 * (1) 构造器私有化  private
 * (2) 自行创建,并且用静态变量保存  static 
 * (3) 向外界提供这个实例  public 
 * (4)强调这是一个单例,我们可以用final 修改
 */
public class Singleton1 {

	public static final Singleton1  INSTANCE = new Singleton1();
	
	private Singleton1(){
		
	}
}

代码中直接实例化Singleton1 对象,也就是在项目启动的时候就为我们创建好了一份Singleton1 对象,这样的优点在于静态对象本身就是单例的,我们在使用的时候可以不考虑线程安全问题。缺点也是显而易见的,在程序初始化时就要为我们创建好这些对象放入到内存中,造成了空间的浪费。

2.饿汉式单例(加载属性文件,初始化静态资源)

public class Singleton2 {

	public static final Singleton2 INSTANCE;
	private String info;
	static{
		Properties properties = new Properties();
		//调用类加载器加载当前的属性文件上
		try {
			properties.load(Singleton2.class.getResourceAsStream("/test.properties"));
			INSTANCE = new Singleton2(properties.getProperty("info"));
			
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	private Singleton2(String info){
		this.info =info;
	}
	public String getInfo() {
		return info;
	}

	public void setInfo(String info) {
		this.info = info;
	}

	@Override
	public String toString() {
		return "Singleton2 [info=" + info + "]";
	}
	
	
}

这种加载方式适用于加载单例对象时初始化数据,比如数据库连接驱动的信息等等,属性properties文件以key value 的形式通过类加载器以及getResourceAsStream获取绝对路径加载。

3.懒汉式单例(非线程安全 舍弃)

/**
 * 懒汉式单例模式(非线程安全)
 * @author yzx
 *
 */
public class Singleton3 {

	private static Singleton3 instance;
	
	private Singleton3() {
	}
	
	public static Singleton3 getInstacne() throws Exception{
		if (instance == null){
			Thread.sleep(500);
			instance = new  Singleton3();
		}
		return instance;
	}
	
}
public class TestSingleion3 {

	public static void main(String[] args) throws Exception {
/*		Singleton3 s1 = Singleton3.getInstacne();
		Singleton3 s2 = Singleton3.getInstacne();*/
		
		//创建一个线程  使用匿名内部类的方式
		Callable<Singleton3> callable = new Callable<Singleton3>() {
			
			@Override
			public Singleton3 call() throws Exception {
				
				
				return Singleton3.getInstacne();
			}
		};
		
		
		//创建线程池
		ExecutorService service = Executors.newFixedThreadPool(2);
		Future<Singleton3> f1 = service.submit(callable);
		Future<Singleton3> f2 = service.submit(callable);
		
		Singleton3 s1 = f1.get();
		Singleton3 s2 = f2.get();
		
		System.out.println(s1==s2);
		System.out.println(s1);
		System.out.println(s2);
		
		service.shutdown();
		
	}	
	

}

由于是在getInstacne 获取单例实例,当有多线程进行调用时,一个线程阻塞(休眠)实例对象还是null,第二个线程进入时同样也为null,就会导致获取线程混乱,出现获取的单例对象不是同一个对象。

4.懒汉式单例(线程安全)

package com.atguigu.single;

/**
 * 懒汉式单例模式 线程安全 且高效
 * 
 * @author yzx
 *
 */
public class Singleton4 {

	private static Singleton4 instance;

	private Singleton4() {
	}

	public static Singleton4 getInstacne() throws Exception {
		// 提高效率加入判断
		if (instance == null) {
			// 避免出现线程安全问题 加入锁
			synchronized (Singleton4.class) {
				if (instance == null) {
					Thread.sleep(1000);
					instance = new Singleton4();
				}
			}
		}
		return instance;
	}

}

这种方式 主要是通过synchronized  关键字加锁,来防止线程安全问题,可是这样又会出现新的问题,造成效率偏低,因此,在进入获取线程的synchronized代码块之前,先判断当前单例对象是否为null 可以保证高效。

5.内部类单例

/**
 * 在内部类被加载和初始化时 才创建INSTANCE对象
 * 静态内部类不会随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的
 * 因为是在内部类加载和初始化时创建的 因此线程是安全的
 * @author yzx
 *
 */

public class Singleton5 {
	
	private Singleton5 (){
		
	}
	//创建内部类加载单例对象
	private  static class Inner{
		private static Singleton5 INSTANCE = new Singleton5();
	}
	public   static  Singleton5 getInstance(){
		return Inner.INSTANCE;
	}
}

在内部类被加载和初始化时 才创建INSTANCE对象 静态内部类不会随着外部类的加载和初始化而初始化,它是要单独去加载和初始化的, 因为是在内部类加载和初始化时创建的 因此线程是安全的。

6.枚举类单例

/**
 * 枚举类型:标示该类型对象时有限的几个
 * 我们可以限定为一个,就成了单例了
 * 
 *
 */
public enum Singleton {
	INSTANCE
}

枚举类的数量定为1就可以构成最简单的饿汉式单例,饿汉式单例(立即加载)方式相同。不过枚举类打印的是类的名字,而饿汉式单例打印的是类的地址。

 

这里只是例举了几种自己总结的单例模式,有问题的地方还请大家及时指出,免的误导了其他同学。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值