`
stanxl
  • 浏览: 4428 次
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

单例模式浅谈

 
阅读更多

所谓单例模式,就是让一个类在该类的外面只有一个实例
我们知道我们一旦把一个类给声明好了,它有多少个实例,取决于我们new多少次
那什么情况下能让一个类在该类的外面只有一个实例呢?
看下面代码:

class Foo{
    private Foo(){}
}
public class Test{
    public static void main(String[] args){ 
    }
}

Ok,为了不让它在外面new,干脆把构造器设置为private,保证了不能new多个,但是一个没有也不合适!!
问题又出现了,这样做的结果是“没例模式”!!!
好吧,这样做虽然外面不能new多个,但是在Foo类里面是可以new的

class Foo{
    private Foo(){}
    public void f1(){
        Foo foo1 = new Foo();
        Foo foo2 = new Foo();
        Foo foo3 = new Foo();
    }
}
public class Test{
    public static void main(String[] args){
    }
}

不能这么去理解,我们所说的单例模式是:不要在Foo类里面new ,而是在Foo类外面只有一个实例!
那肿么办呢?
往下看:

class Foo{
    //饱汉式
    private static Foo instance = new Foo();
    private Foo(){}
    public static Foo getInstance(){
        return instance;
    }
}
public class Test{
    public static void main(String[] args){ 
        Foo f1 = Foo.getInstance();
        Foo f2 = Foo.getInstance();
        System.out.println(f1 == f2);
    }
}

解释一下吧:

private static Foo instance = new Foo();
//当一个类作为另一个类的属性时,叫做关联,而另一个类又是它自己时,就构成了自关联
//将这个属性设置为static ,静态属性隶属于静态块
//而静态块在整个程序运行期间,只会运行一次,那么什么时候运行呢?
//调用一个类的静态属性、静态方法时,或是在外面new的时候
//而现在在外面调用了Foo的静态方法getInstance()

现在在main方法里第一次调用了getInstance()方法,这个时候会去加载类
加载类的时候,先走静态块,也就是先执行上面的属性,此时new一个实例,再往下走,经过了构造器,构造器里什么也没执行
最后再执行对外暴露的一个公共方法,返回一个实例
当第二次调用getInstance()方法时,静态块不会再重新加载,保证了只new一次,这次的引用指向的仍是之前的对象
所以最后我们在比较f1 == f2两个的引用地址时,结果是true;
那么我们先把这种称为“饱汉式”,因为它一进来就new了
看下面的懒汉式:

class Foo{
    //懒汉式
    private static Foo instance = null;
    private Foo(){}
    public static Foo getInstance(){
        if(instance == null){
            instance = new Foo();
        }
        return instance;
    }
}
public class Test{
    public static void main(String[] args){ 
        Foo f1 = Foo.getInstance();
        Foo f2 = Foo.getInstance();
        System.out.println(f1 == f2);
    }
}

分析一下:
我们知道静态属性是隶属于整个类,而不是属于单独的某个实例
当在main方法里第一次调用getInstance()方法时,过程和上面一样,第一次是null,此时创建一个实例,当第二次调用getInstance()方法时,此时的instance已不是null,不会在进if(),保证了只new一次
由于这种方式,在用的时候,才去new,不用的时候,不new,就把它称为“懒汉式

现在这个程序有点小小的问题,假设这个方法被两个线程调用,当第一个线程顺利通过了if(),正准备new实例时,是有机率被第二个线程所打断的,第二个线程此时new了一个实例,而当此时第一个线程再次努力的抢占cpu成功,它会再从这里继续往下执行,也new一个实例,这个时候,问题就出现了,这就不叫单例了……………
OK,问题暴露出来了,肿么办呢?
现在只能加对象互斥锁了

public static Foo getInstance(){
        synchronized(Foo.class){
            if(instance == null){
                instance = new Foo();
            }
        }
        return instance;
}

当第一次调用getInstance()方法,第一个线程获得了锁,顺利通过if(),此时被第二个线程抢走了cpu,但是第二个线程得不到锁,因为锁在第一个线程手里,所以现在第二个线程只能放弃cpu,让第一个线程顺利往下执行,执行完后,释放cpu,如果另外的线程再拿到锁时,此时instance 已经不是null,就不会再去new了,保证了只new一次,单例模式就OK了

但是它还不够完美,因为每次一个线程过来时候,都要加锁,其实只要第一次加锁就够了,当instance != null,时就没有必要加锁了
所以看下面:

public static Foo getInstance(){
    if(instance == null){//保证只加一次锁
        synchronized(Foo.class){
            if(instance == null){//保证只new一次
                instance = new Foo();
            }
        }
    }
        return instance;
}

好了,这就是双重检测机制,现在看起来有点完美了
如有瑕疵,请多指教…………

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>

版权声明:本文为博主原创文章,未经博主允许不得转载。

分享到:
评论

相关推荐

    浅谈Spring单例Bean与单例模式的区别

    "浅谈Spring单例Bean与单例模式的区别" 本文主要介绍了Spring单例Bean与单例模式的区别,通过对比两者的定义、实现机制和应用场景,帮助读者更好地理解这两种概念的异同。 一、单例模式的定义和实现 单例模式是一...

    C#单例模式视频讲解

    《C#面向对象设计模式纵横谈(2):Singleton 单件(创建型模式)》这个视频教程很可能深入浅出地介绍了单例模式的概念、实现方式以及在实际项目中的应用案例,对于学习和掌握单例模式是很好的参考资料。通过观看和实践...

    浅谈C#单例模式的实现和性能对比

    C#单例模式的实现和性能对比 单例模式是软件工程中使用最多的几种模式之一,它是一种只能存在一个实例的类,在C#中,更准确的说法是在每个AppDomain之中只能存在一个实例的类。单例模式的实现方式有很多种,本文将...

    浅谈Python反射 & 单例模式

    在Python编程中,反射和单例模式是两种重要的设计原则,它们分别用于增强代码的动态性和控制类实例的创建。 **反射** 是一种允许程序在运行时检查和修改自身行为的能力。在Python中,由于其动态特性,反射得以广泛...

    浅谈java单例设计模式

    单例模式 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象: 将类的...

    浅谈Java编程中的单例设计模式

    在日志工具类的场景中,单例模式的应用尤为恰当,因为它避免了因频繁创建日志对象而导致的内存浪费。 原始的日志工具类`LogUtil`提供了一个简单的日志级别控制功能,通过静态方法`debug`, `info`, `error`根据预设...

    设计模式浅谈种PPT学习教案.pptx

    本文将围绕“设计模式浅谈”这一主题,深入探讨设计模式的基本概念、核心原则以及在实际应用中的作用,特别关注创建型设计模式的介绍与应用。 首先,我们不得不面对这样一个现实:在软件开发过程中,代码的紧密耦合...

    浅谈Java中单例设计模式之构造方法私有化.pdf

    这样的设计迫使我们通过类内部提供的公共静态方法来获取类的实例,而这正是单例模式的精华所在——提供一个全局访问点来获得类的唯一实例。 访问控制修饰符是Java语言中用于控制类、变量、方法的访问权限的关键词。...

    设计模式浅谈24种.ppt

    在《设计模式浅谈24种》中,主要讨论了24种经典的GOF设计模式,这些模式被分为三大类:创建型、结构型和行为型。 创建型模式主要关注对象的创建,包括以下几个核心模式: 1. **Abstract Factory**(抽象工厂)模式...

    浅谈JAVASE单例设计模式

    在Java中,单例模式的应用广泛,尤其是在处理系统级服务、配置信息、线程池等需要全局共享的对象时。下面我们将深入探讨单例设计模式的实现方式和注意事项。 1. **饿汉式单例** 饿汉式单例在类加载时就完成了实例...

    C#面向对象设计模式纵横谈 12种设计模式

    本篇将深入探讨12种核心的设计模式,它们是:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式、适配器模式、装饰器模式、代理模式、桥接模式、组合模式、享元模式和观察者模式。 1. **单例模式**:确保一...

    从追MM浅谈Java的23种设计模式

    - 单例模式:确保一个类只有一个实例,并提供全局访问点。 - 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。 - 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定...

    浅谈OOP之uml设计模式

    例如,单例模式在C#中可以使用`static`关键字和`lock`语句来实现线程安全的单例;工厂模式可以通过接口和抽象类定义,然后由具体的实现类完成对象的创建;装饰器模式则通过继承并添加新的行为来扩展已有类的功能。 ...

    C#面向对象设计模式纵横谈-创建型模式

    单例模式常用于配置文件管理、线程池或数据库连接等场景,确保在整个应用程序中只有一个共享的实体。 2. **工厂模式(Factory Method)** 工厂模式定义了一个用于创建对象的接口,但让子类决定实例化哪一个类。它...

    浅谈Spring 的Controller 是单例or多例

    由于单例模式的特性,Controller的实例在系统启动后被创建,然后在每次请求时被共享,因此在定义Controller时应遵循一些最佳实践: 1. **避免在Controller中定义成员变量**:由于单例Controller可能会被多个线程...

    浅谈设计模式在Android代码重构中的应用.pdf

    文章通过具体的Android应用开发案例,展示了如何运用设计模式进行代码重构,例如,通过使用单例模式管理网络请求,使用工厂模式简化对象创建,使用观察者模式实现数据的实时更新等。 5. 提升信息素养的重要性 在...

    浅谈spring中用到的设计模式及应用场景

    浅谈Spring中用到的设计模式及应用场景 Spring 框架是 Java 企业级应用程序的核心组件之一,它提供了强大的功能和灵活的架构来简化企业级应用程序的开发。Spring 框架使用了多种设计模式来实现其功能,本文将对 ...

Global site tag (gtag.js) - Google Analytics