设计模式----单例模式

单例模式

单例模式简介

       单例模式是一种创建型模式,它提供了一种创建对象的最佳方式。单例类只能有一个实例,并提供一个访问它的全局访问点。

单例模式的优点
  • 在内存中只会存在一个实例,这样大大减少了内存的开销,尤其是频繁的创建和销毁实例
  • 避免了对资源的多重占用,节约资源。
  • 提供了对唯一实例的受控访问。
单例模式的缺点
  • 不适用于变化的对象,如果同一类型的对象需要在不同的应用场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
  • 没有接口,不利于扩展。
  • 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎样实例化。
单例模式的应用场景
  • Servlet中每一个servlet的实例
  • 配置xml文件时bean对象的获取
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源,数据库软件系统使用数据库连接连接池,主要节省打开和关闭数据库连接所引起的效率损耗。
  • 多线程的线程池设计一般也是采用单例模式,这是由于线程池要方便对线程进行控制。
  • 应用程序的日志应用一般也是采用单例模式,因为共享的日志文件一直处于打开状态,因此只能有一个实例进行操作,否则容易出问题。
单例模式的实现方式
  • 懒汉模式
  • 饿汉模式
  • 双重校验锁
  • 登记式/静态内部类
  • 枚举
懒汉模式
package com.jbc.singleton;
/**
 * 单例模式-懒汉模式
 * 实现了延迟加载
 * 线程安全,但是由于在getInstance方法上使用了同步锁,每次调用这个方法都会同步,影响效率
 * 不推荐使用
 * @author sharbee
 *
 */
public class SingletonInstance1 {
	//1.声明此类型的变量,但是不实例化
	private static SingletonInstance1 instance;
	//2.私有化构造方法,避免通过new关键字创建实例
	private SingletonInstance1(){}
	//3.对外提供一个获取实例的静态方法,加上synchronized关键字,保证数据安全
	public static synchronized SingletonInstance1 getInstance1(){
		if(instance==null){
			return new SingletonInstance1();
		}
		return instance;
	}
}
饿汉模式
package com.jbc.singleton;
/**
 * 单例模式-饿汉模式
 * 线程安全
 * 没有实现延迟加载
 * 这种方式是基于类加载机制来解决多线程的问题的,因为类在加载时线程互斥。
 * 缺点:只要类加载就会创建实例,浪费内存.
 * @author sharbee
 *
 */
public class SingletonInstance2 {
	//1.声明该类型的变量,并且实例化,在类加载的过程中创建了实例
	//由于在类加载的过程中是线程互斥的,所以保证了线程安全
	private static SingletonInstance2 instance2 =new SingletonInstance2();
	//2.私有化构造方法,防止用new关键字直接创建对象实例
	private SingletonInstance2(){}
	//创建一个静态方法获取实例。
	public static SingletonInstance2 getInstance2(){
		return instance2;
	}
}
双重校验锁

双重校验锁这里将synchronized关键字写在了if条件内部,解决了饿汉模式下每次调用方法都会实现同步浪费资源的问题。这里一共进行了两次if判断
第一次判断:这里主要是为了解决饿汉模式下浪费资源的问题,进行一次判断,倘若不为null,就不会再进入同步锁中。
第二次判断:假设没有进行第二次判断,那么当前线程t1执行了第一次判断后,判断实例为空,假设线程t2也获取了cpu执行权力进入了方法,第一次判断,实例为空,这时t1又获取了cpu的控制权,获取了锁,创建了实例,释放锁。然后t1又获取cpu的执行权力,由于之前已经对第一个if进行了判断,不再进行判断,获取锁,有创建了实例,就会导致创建多个实例。所以需要在同步块中进行第二次判断,确保只创建一个实例。
注意

private static volatile SingletonInstance3 instance3=null;需要加volatile关键字,否则会出现错误。问题的原因在于JVM指令重排优化的存在。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。

package com.jbc.singleton;
/**
 * 
 * 双检锁/双重校验锁(DCL,即 double-checked locking)
 * 线程安全
 * 实现了延迟加载
 * @author sharbee
 *
 */
public class SingletonInstance3 {
	//1.声明该类型的变量
	private static volatile SingletonInstance3 instance3=null;
	//2.私有化构造方法
	private SingletonInstance3(){}
	//3.创建一个获取实例的静态方法
	public static SingletonInstance3 getInstance3(){
		if(instance3==null){
			synchronized(instance3){
				if(instance3==null){
					return new SingletonInstance3();
				}
			}
		}
		return instance3;
	}
}
登记式/静态内部类
package com.jbc.singleton;
/**
 * 类似饿汉模式,都是基于类加载机制实现的
 * 线程安全
 * 有延时加载
 * @author sharbee
 *
 */
public class SingletonInstance4 {
	private static class SingletonClassGetInstance4{
		//创建一个实例,加上final关键字,保证这个实例是不变的
		private static final SingletonInstance4 instance4=new SingletonInstance4();
	}
	//私有化构造方法
	private SingletonInstance4(){}
	public static final SingletonInstance4 getInstance4(){
		return SingletonClassGetInstance4.instance4;
	}
}
枚举
public enum SingletonInstance5 {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值