1,什么是单例模式?
一句话:确保一个类只能new一个对象,并且这个对象要能在整个系统中都能访问。
2,从老板到职员。
单例模式有以下几点要求:
A,构造函数一般为private;
B,通过静态方法或者枚举返回唯一实例;
C,确保多线程情况下也只能有一个实例;
D,确保单例类在反序列化的情况下不会多次实例化对象。
从程序员被老板压榨说起:
公司里会有一个永远的老大,我们称之为CEO;
还有很多个部门经理,我们称之为Manager;
还有很多温饱线上的码农,我们称之为Programmer;
但是,一个公司的最终决定权只有一个人有,那就是CEO,这里我们把CEO作为单例模式类。下面我们来看一下UML图和具体实现的代码。
package com.cssrc.SingletonPattern;
public class CEO extends Staff{
private CEO(){
}
public static CEO getCeo(){
return SingleCEO.ceo;
}
@Override
public void work(Staff staff) {
System.out.println("CEO:"+getCeo());
System.out.println("Manager:"+staff);
System.out.println("CEO安排项目经理干活");
}
/**
* 静态内部类实现单例模式
* @author liuyangchao
*/
private static class SingleCEO{
private static final CEO ceo = new CEO();
}
}
package com.cssrc.SingletonPattern;
public class Manager extends Staff{
@Override
public void work(Staff staff) {
System.out.println("Manager:"+this);
System.out.println("Programmer:"+staff);
System.out.println("项目经理安排程序员干活");
}
}
package com.cssrc.SingletonPattern;
public class Programmer extends Staff{
@Override
public void work() {
System.out.println("Programmer:"+this);
System.out.println("程序员干活");
}
}
package com.cssrc.SingletonPattern;
public class Staff {
public void work(){
}
public void work(Staff staff){
}
}
package com.cssrc.SingletonPattern;
public class Test {
public static void main(String[] args){
CEO ceo1 = CEO.getCeo();
CEO ceo2 = CEO.getCeo();
Manager manager1 = new Manager();
Manager manager2 = new Manager();
Programmer pro1 = new Programmer();
Programmer pro2 = new Programmer();
Programmer pro3 = new Programmer();
Programmer pro4 = new Programmer();
ceo1.work(manager1);
ceo2.work(manager2);
manager1.work(pro1);
manager1.work(pro2);
manager1.work(pro3);
manager2.work(pro4);
pro1.work();
pro2.work();
pro4.work();
}
}
测试结果:
CEO:com.cssrc.SingletonPattern.CEO@2a9931f5
Manager:com.cssrc.SingletonPattern.Manager@2f9ee1ac
CEO安排项目经理干活
CEO:com.cssrc.SingletonPattern.CEO@2a9931f5
Manager:com.cssrc.SingletonPattern.Manager@67f1fba0
CEO安排项目经理干活
Manager:com.cssrc.SingletonPattern.Manager@2f9ee1ac
Programmer:com.cssrc.SingletonPattern.Programmer@3fbefab0
项目经理安排程序员干活
Manager:com.cssrc.SingletonPattern.Manager@2f9ee1ac
Programmer:com.cssrc.SingletonPattern.Programmer@133c5982
项目经理安排程序员干活
Manager:com.cssrc.SingletonPattern.Manager@2f9ee1ac
Programmer:com.cssrc.SingletonPattern.Programmer@5f186fab
项目经理安排程序员干活
Manager:com.cssrc.SingletonPattern.Manager@67f1fba0
Programmer:com.cssrc.SingletonPattern.Programmer@3d4b7453
项目经理安排程序员干活
Programmer:com.cssrc.SingletonPattern.Programmer@3fbefab0
程序员干活
Programmer:com.cssrc.SingletonPattern.Programmer@133c5982
程序员干活
Programmer:com.cssrc.SingletonPattern.Programmer@3d4b7453
程序员干活
可见,CEO始终只实例化了一个对象。具体代码放在本文最后,可下载。
3,剖析单例模式。
在我看相关单例模式的文章中,关于双重锁定方法(DCL)实现单例模式有一个volatile关键字的问题,我们就来罗列一下常见的几种单例模式的实现方法:
A,懒汉模式
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
这种模式其实就是生命了一个静态对象,同时为了保证多线程情况下单例对象唯一性,使用了synchronized关键字。这样写实没有问题的,但是在性能上会有不足,比如,instance已经初始化完成了,但是每次调用getInstance()这个方法时还是会进行同步,这样就消耗了不必要的资源。
B,DCL,double click lock,双重锁定
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized (instance) {
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
DCL和懒汉模式唯一的区别就是在instance第一次初始化的时候去同步执行,生成一个唯一的实例。第一层NULL判断是为了判断是否实例化过,第二重判断是为了区分在多线程情况下instance混乱的情况。但是这样写是有问题,问题就在于instance = new Singleton();这个语句的执行过程。假设线程A执行到了这条语句,编译成汇编指令之后大致做了三件事情:
a,给Singleton的实例分配内存
b,调用Singleton(),初始化成员字段
c,把instance对象指向分配的内存空间(这个时候instance!=null)
java编译器允许处理器乱序执行,以及java内存模型中Cache、寄存器到主内存回写顺序的规定,2,3顺序无法保证顺序执行。所以在JDK1.5以后出现了volatile关键字,只需要把instance定义成private volatile static Singleton instance = null就可以保证每次instance都是从主内存读取的了。但是,在高并发情况下还是不能完全保证唯一性,但是几率很小。
C,静态内部类单例模式(推荐)
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return singletonHolder.instance;
}
private static class singletonHolder{
private static final Singleton instance = new Singleton();
}
}
这种模式只会在使用instance的时候才会去调用,同时不会影响性能,是懒汉模式和DCL的一种综合。
代码下载:http://download.csdn.net/detail/js_liuyangchao/9567220