【设计模式】单例模式(一)

一、简介

   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
若有错误之处,请多多谅解并欢迎批评指正。    
本博客中未标明转载的文章归作者小毛驴所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

图解Java设计模式(一)

02-27
1) 优秀的程序应该是这样的:阅读时,感觉很优雅;新增功能时,感觉很轻松;运行时,感觉很快速,这就需要设计模式支撑。 2) 设计模式包含了大量的编程思想,讲授和真正掌握并不容易,网上的设计模式课程不少,大多讲解的比较晦涩,没有真实的应用场景和框架源码支撑,学习后,只知其形,不知其神。就会造成这样结果: 知道各种设计模式,但是不知道怎么使用到真实项目。本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,比如 模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等 3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧 课程内容和目标 本课程是使用Java来讲解设计模式,考虑到设计模式比较抽象,授课采用 图解+框架源码分析的方式 1) 内容包括: 设计模式七大原则(一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式模式(8种实现)、抽象工厂模式、原型模式、建造者模式、工厂模式。结构型模式:适配器模式(3种实现)、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式(3种实现)。行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式) 2) 学习目标:通过学习,学员能掌握主流设计模式,规范编程风格,提高优化程序结构和效率的能力。
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值