单例模式---从职员层次谈起

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值