第八篇:单例模式

关于单例模式我并不想做过多解释,网络上有一大堆关于它的定义,简而言之,单例就是指单个对象,整个程序的运行生命周期内某个对象只会生成一次!

这么做有什么意义呢?很简单,你的电脑开机,有多个回收站对象会有什么后果?你删掉一个文件它该被放入哪个回收站? 有多个注册表会怎样? 程序中有多个数据库连接池会怎样? 这些问题留给你去思考吧!

正因为某些对象的特殊性,整个程序生命周期内只能出现一个它的实例, 又或者,某些对象的创建非常复杂非常消耗资源,且它是不会随着程序的运行而有所改变的,那么我们也期望只生产一次这个对象,后面直接拿来复用! 那么这种只会生成一次某个对象的编码技巧,我们就将其称为单列模式!

网络上好像说是有6中实现单例的方法,但我个人认为只有4种!另外2种是网友的代码演变,压根算不上... 当然,最有效好用的只有一种,我稍后将代码贴出来!

OK,让我们拿出点干货来,让代码来说话吧!


第一种:饿汉式(所谓饿汉就是指不管有没有人要使用我,我先把自己给创建出来)

public class Singleton {
	
	/**饿汉式,直接创建对象*/
	private static final Singleton singleton = new Singleton();
	
	/**构造器私有化,这样别人就无法创建我!*/
	private Singleton() {}
	
	/**必须提供一个让客户端获取自身实例的方法*/
	public static Singleton getInstance(){
		return singleton;
	}
	
	
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1==s2); //===>>>输出:true
	}
}

如上面代码所示,我们的Singleton类的实例只可能有一个,不用担心线程问题,因为它是随着类加载而实例化的,而类只会被加载一次(当然,也可以被加载多次,关于这点我打算后面再写篇让同一个类加载多次的文章,这里略过) ; 虽然它是安全的,但也有一个问题,假设这个类的创建非常复杂非常消耗资源,但是由于是饿汉式,不管有没有真正要使用这个类的实例,它总是会创建一次自己,从而占用我们的系统资源,假如压根没有人用到它,那就会造成资源浪费,由此,下面的懒汉式诞生了!


第二种:懒汉式(所谓懒汉式是指等到有人要使用我时,我就开始创建一次自己)

public class Singleton {
	
	/**懒汉式,等到有人使用时再创建!*/
	private static Singleton singleton ;
	
	/**构造器私有化,这样别人就无法创建我!*/
	private Singleton() {}
	
	/**必须提供一个让客户端获取自身实例的方法*/
	public static Singleton getInstance(){
		//判断是否为空,如果是第一次被人使用,则必定是空
		if( singleton==null ){
			singleton = new Singleton();
		}
		return singleton;
	}
	
	
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1==s2); //===>>>输出:true
	}
}
如上面代码所示,我们的Singleton现在不再随着类的加载而实例化了,它只有等到真正有人用到它时才会去实例化一次,这归功于那句if...else...的功能,但也正是由于if...else...这里的代码所在,它被引入了线程不安全的隐患,在多个线程同时调度下,假如A,B两个线程都进入到了getInstance()中,此时singleton为空,那么就会被创建2次,这是我们无法容忍的,于是,有了下面的双重加锁的代码实现!


第三种:懒汉式变种(双重检查加锁)

public class Singleton {
	
	/**懒汉式,等到有人使用时再创建!*/
	private static Singleton singleton ;
	
	/**构造器私有化,这样别人就无法创建我!*/
	private Singleton() {}
	
	/**必须提供一个让客户端获取自身实例的方法*/
	public static Singleton getInstance(){
		if( singleton==null ){
			//加一把锁,且再进行一次判断
			synchronized (Singleton.class) {
				//当A线程执行到这里,if条件成立,singleton将被实例化,
				//A线程执行完synchronized代码块,释放锁
				//而后B线程允许放行进入到这里,此时if条件将不再成立!
				if( singleton==null ){
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}

}
如果我们直接在getInstance()方法上加锁(有人将这种做法也称之为一种实现方式...所以有人说有6种实现方式),那么并发上来,将会造成多个线程同时等待,而将其放入if条件之中,将会大量减少线程等待(因为由于上一个线程的执行完毕,if条件将不再成立),同时也保证了多线程下该对象实例只会被创建一次! 但无论如何,都会造成线程等待,那么有没有一种更好的方式来实现单例呢?既可以不造成线程等待,同时又是安全的,保证只会创建一次对象,同时又是基于懒汉式的创建方式, Ok,终极方法,内部类实现!


第四种:懒汉式变种(内部类实现单例,我认为是最好的一种)

public class Singleton {
	
	/**构造器私有化,这样别人就无法创建我!*/
	private Singleton() {}
	
	//使用内部类!
	private static class Inner{
		private static final Singleton singleton = new Singleton();
	}
	
	/**必须提供一个让客户端获取自身实例的方法*/
	public static Singleton getInstance(){
		return Inner.singleton;
	}
	
	
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1==s2); //===>>>输出true
	}
}
OK,我们来研究一下上面的代码吧!我们的Inner是一个静态内部类,就算有一亿个线程,它也只会被类加载器装载一次,而里面的singleton会随着Inner的类装载而被实例化,所以,它绝对是唯一不重复的,这一点,它和饿汉式的效果是一样的。但和饿汉式不同的时,Inner类只有当我们调用Singleton类的getInstance方法时才会被类加载器加载,所以,singleton的实例也只会在这个时候被创建,所以它不会造成资源浪费!如果理解不了,给我留言吧,文字我只能说到这啦!


Ok,喝杯咖啡润润喉...拜拜...


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值