设计模式单例模式

Posted 程序员小毛驴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式单例模式相关的知识,希望对你有一定的参考价值。

一、简介

   1.1 定义

  Ensure a class has only one instance, and provide a global point of access to it.
  确保某个类只有一个实例,并提供一个全局访问点。

   1.2 UML类图


Singleton是单例类。
实现单例模式的几个关键点:
1、构造函数(Singleton())不对外开放,一般为private。
2、通过一个静态方法(getInstance)返回对象。
3、确保单例对象只有一个,尤其是在多线程环境下。

   1.3 有什么用处?

 有一些对象我们只需要一个,比如:线程池、缓存、日期时间( Calendar)对象等等,避免产生多个对象消耗过多的资源。

   1.4 简单示例-懒汉模式

package com.lll.design;

public class Singleton 

	private static  Singleton instance;  
	private Singleton()  

	  
	public static Singleton getInstance()      
		if(instance == null)//1
		     instance = new Singleton();//2
		  
		return instance; //3
	  

 单例模式比较简单,只有一个单例类。如果是在单线程下,上述代码是没有问题的。如果是多线程呢?可以来测试下:

package com.lll.design;

public class Singleton 

	private static  Singleton instance;  
	private Singleton()  

	  
	public static Singleton getInstance()      
		if(instance == null)         
			try 
				Thread.sleep(1000);//加个休息时间,让多个线程同时进来
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			instance = new Singleton();
		  
		System.out.println(instance);
		return instance;               
	  

 Client类:

package com.lll.design;

public class Client 
	public static void main(String[] args) 
		for(int i = 0;i < 10;i++)
		
			new Thread(new Runnable() 
				
				@Override
				public void run() 
					Singleton.getInstance();
				
			).start();;
		
		
	
控制台:
+------------------------------------------------------------------+

com.lll.design.Singleton@4dbac721

com.lll.design.Singleton@50d03d7f

com.lll.design.Singleton@50d03d7f

com.lll.design.Singleton@50d03d7f

com.lll.design.Singleton@4dbac721

com.lll.design.Singleton@50d03d7f

com.lll.design.Singleton@50d03d7f

com.lll.design.Singleton@50d03d7f

com.lll.design.Singleton@4dbac721

com.lll.design.Singleton@50d03d7f

+------------------------------------------------------------------+

 从控制台输出结果可以看出以上代码在多线程环境下执行,出现了至少一个以上不同的实例。原因是多个线程同时进入了//1,并且都创建了对象。那么如何防止多个线程同时对一段程序进行操作,你可能想到synchronized。因为可以保证当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行,而另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

   1.5 解决办法一 synchronized

public static Singleton getInstance()      
     if(instance == null)       
	  synchronized (Singleton.class) 
		instance = new Singleton();
	  
        
      return instance;               
  
 然而这样并不能解决问题,当 instance为null 时,两个线程可以并发地进入if 语句内部,还是会创建2个对象如下图所示:
 哦,问题根源在第一次释放锁后没有再去判断instance是否为null,继续修改代码。
public static Singleton getInstance()      
	if(instance == null)       
		synchronized (Singleton.class) 
			if(instance == null)       
				
					instance = new Singleton();
				
			
		
	  
	return instance;               
  

 看起来好像没有什么问题了,除了第一次创建对象之外,其他的访问在第一个if中就返回了,因此不会走到同步块中。没有问题了吗?有的,在Java中new Singleton并不是像我们看到的一行代码这么简单,这句话在JVM执行的时候被编译成8条汇编指令,大致做了3件事情:
1、在堆中给Singleton的实例分配内存。 
2、初始化Singleton的构造器 。

3、将instance指向分配的内存空间(这个时候instance已经是非空的了)。
 由于Java编译器允许处理器乱序执行(out-of-order),上面的第二点和第三点的顺序是无法保证的,也就是说,执行顺序可能是1-2-3也可能是1-3-2,如果是后者,并且在3执行完毕、2未执行之前,被切换到线程二上,这时候instance因为已经在线程一内执行过了第三点,instance已经是非空了,所以线程二直接拿走instance但是2还未执行,这样就出问题了。那么如何解决这个问题呢?代码如下:

package com.lll.design;

public class Singleton 

	private volatile static  Singleton instance;  
	private Singleton()  

	  
	public static Singleton getInstance()      
		if(instance == null)       
			synchronized (Singleton.class) 
				if(instance == null)       
					
						instance = new Singleton();
					
				
			
		  
		return instance;               
	  

  使用volatile关键字保证每次instance都从主内存读取,这样多少会有些性能上的丢失。有没有更好的办法呢?有,推荐使用静态内部类和枚举二种单例模式。关于volatile还不太熟悉的同学请看我这篇文章:【Java并发编程】深入分析volatile(四)

   1.6 静态内部类单例模式

package com.lll.design;

public class Singleton 

	private Singleton()  

	  
	public static Singleton getInstance()      
		return SingletonHolder.instance;               
	  
	private static class SingletonHolder
	
		private static final Singleton instance = new Singleton();
	

  静态内部类和懒汉模式一样都是延迟了单例的实例化,但是静态内部类更佳,它不用在每次调用getInstance方法时都要去同步阻塞、加锁和释放锁,造成一定资源的浪费。

   1.7 枚举单例模式

package com.lll.design;

public enum SingletonEnum 
	INSTANCE;
	public void doingSome()
	
		System.out.println("小毛驴说:枚举单例模式就是这么简单。。。");
	

  枚举最大的优点就是写法简单、简洁、易用,并且绝对保证不会被多次实例化,即使是在面对复杂的序列化或反射攻击的时候也这样。

作者: 小毛驴,一个游戏人 
梦想:世界和平   
原文地址: http://blog.csdn.net/liulongling 若有错误之处,请多多谅解并欢迎批评指正。      本博客中未标明转载的文章归作者 小毛驴所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

CSDN 社区图书馆,开张营业! 深读计划,写书评领图书福利~

以上是关于设计模式单例模式的主要内容,如果未能解决你的问题,请参考以下文章

小功能⭐️Unity单例模式

闲来无事做,把最近碰到的一些小面试题整理一下,单例模式,冒泡排序,递归

设计模式之单例模式

单例模式的9中实现方式

文成小盆友python-num8 面向对象中的成员,成员修饰符,特殊成员,异常处理,设计模式之单例模式

单例模式(Singleton)