【第22期】观点:IT 行业加班,到底有没有价值?

设计模式系列之一:单例模式(Singleton Pattern)

原创 2015年11月19日 22:15:32

这一系列的文章我会从代码的思路上介绍目前编程中存在的编程模式,并且赋予这些代码的一些实例场景。

单例模式,即单实例模式,即在程序运行生命周期内。要么不存在一个类的实例(这里有些不严谨,理论上来说是否存在取决于代码设计和是否调用),要么只存在全局一个实例对象。

这样的例子在程序设计中有很多。对于某些特殊的类,在程序中只能new出一个实例,如果多个实例的话,暂且不说线程安全问题,从程序逻辑上面也会有很多问题。

比如我们只有一台打印机:
public class Printer{
        /**
         * Is Printer Running?
         * */
        private boolean isRunning;

        /**
         * Default Constructor
         * */
        public Printer(){

        }
        public void setRunning(boolean isRunning){
            this.isRunning = isRunning;
        }
    }

那么我们就不能在需要的时候都来创建一个新的实例,尤其是当一个实例有相应状态的时候(比如打印机在Isrunning = true的状态时候,此时该打印机是不能接受新任务的),所以此时情况我们需要创建一个全局唯一的实例来帮助我们完成程序逻辑。

  • 外界不能随意创建实例->构造方法私有
  • 外界不能创建,所以类需要自己创建唯一实例->类中需要维护自身实例的静态变量
  • 外界可以获取创建的静态变量->用公开静态方法获取静态实例

基于以上三个原则,我们重新编写上面的代码:

/**
 * Created by youweixi on 15/11/19.
 */
public class Printer{
    /**
     * Is Printer Running?
     * */
    private boolean isRunning;
    /**
     * Static Self
     * */
    private static Printer printer;

    /**
     * Default Constructor
     * */
    private Printer(){

    }

    /**
     * Public GetInstance
     * */
    public static Printer getInstance(){
        if (printer == null){
            printer = new Printer();
        }
        return printer;
    }

    public void setRunning(boolean isRunning){
        this.isRunning = isRunning;
    }
}

这样我们在程序中就不能通过new来创建Printer对象。只能通过getInstance方法来获取静态唯一实例。

但是上面的代码是存在问题的。

我们知道,程序在CPU上面是相互争夺时间片来运行的,即多个异步过程(进程、线程)之间的代码运行顺序是不确定的。

如果有两个代码片段同时需要调用getInstance来获取实例:

  1. 此时第一个线程发现printer == null是成立的,时间片耗尽,CPU转向另一个线程。
  2. 另一个线程也进入函数,判断printer == null仍然成立,分配实例,然后继续运行接下来代码。
  3. 此时时间片耗尽,CPU转向第一个线程,线程重新分配了一个新的实例造成多实例覆盖情况。

我们可以看到上面的单例模式在多线程中会发生问题,原因在于调用的方法和单例创建的时间。基于这两个原因,我们可以对上面的单例模式进行改进。

1. 提前单例创建时间

public class Printer{
    /**
     * Is Printer Running?
     * */
    private boolean isRunning;
    /**
     * Static Self
     * */
    private static Printer printer = new Printer();

    /**
     * Default Constructor
     * */
    private Printer(){

    }

    /**
     * Public GetInstance
     * */
    public static Printer getInstance(){
        return printer;
    }
}

即在编译时候创建静态变量,这样的话,在调用getInstance方法的时候不必再进行检测创建。这样的话可能会带来资源浪费,尤其是在很长一段时间内没有发生实例获取的项目中。

2. 对getInstance加同步控制synchronized

/**
     * Public GetInstance
     * */
    public static synchronized Printer getInstance(){
        if (printer == null){
            printer = new Printer();
        }
        return printer;
    }

这样的话可以保证每次只有一个线程拿到方法锁进入方法。但是这种方式会带来性能损失。如果工程中频繁通过调用getInstance获取实例而出现等待方法锁。

3. 二次检测
所谓的二次检测即是对方法二的改进,进行第一次检测的时候过滤大部分请求已经存在静态实例的请求,第二次检测才真正进行同步块中的静态实例分配。可以说这种方法是最安全和节省资源的。

/**
     * Public GetInstance
     * */
    public static Printer getInstance(){
        if (printer == null){
            synchronized (this){
                if (printer == null){
                    printer = new Printer();
                }
            }
        }
        return printer;
    }

这样的话即时有两个程序同时进入printer==null的程序逻辑也还是会只有一个会拿到内层检测创建的同步锁完成第一次创建。

版权声明:本文为本人原创,未经同意谢绝转载。 举报

相关文章推荐

Android设计模式系列(3)--SDK源码之单例模式

http://www.cnblogs.com/qianxudetianxia/archive/2011/08/07/2130306.html   此博客的设计模式系列值得好好看看....... ...

设计模式笔记7:单例模式(Singleton Pattern)

一、单例模式的内容 单例模式确保一个类只有一个实例,并提供全局访问点。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。 二、单例模式特点 某个类只能有一个实例。 <sp

程序员升职加薪指南!还缺一个“证”!

CSDN出品,立即查看!

设计模式系列(八)单例模式(Singleton Pattern)

设计模式系列(八)单例模式(Singleton Pattern)     单例模式就是确保一个类只有一个实例,并提供一个全局的访问点。具体来看,就是指定义的某个类,在程序运行期间,只允许有一个实例...

设计模式 之 “单例模式[Singleton Pattern]”

单例模式[Singleton Pattern]类图 [img]http://dl.iteye.com/upload/attachment/266520/46c5fc30-682e-33a7-95b6-21c76714a3d6.png[/img] 这里我们来看下这个单例模式注册器如何工作的: ...

乐在其中设计模式(C#) - 单例模式(Singleton Pattern)

[索引页][源码下载]乐在其中设计模式(C#) - 单例模式(Singleton Pattern)作者:webabcd介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点。示例保证一个类仅有一个实...
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)