手机版
你好,游客 登录 注册 搜索
背景:
阅读新闻

设计模式之 Java 中的单例模式(Singleton)

[日期:2014-06-22] 来源:Linux社区  作者:Linux [字体: ]

目录

1、单例(Singleton)模式:
2、最简单的单例模式:
3、进阶:
4、延迟创建 :
5、线程安全 :
6、如何创建并发访问效率高的单例 : Double-Check Locking
7、完整的测试用例如下:
8、Initialization on demand holder
9、单例模式序列化应该注意的问题: Singleton 的序列化
10、推荐阅读:

1、单例(Singleton)模式:

保证一个类在系统里只能有一个对象被实例化。

如:缓存池、数据库连接池、线程池、一些应用服务实例等。

难点:在多线程环境中,保证实例的唯一性。

2、最简单的单例模式:

保证该类构造方法是私有的,外部无法创建该类型的对象;

提供一个全局访问点,方便给客户对象提供对此单例对象的使用;

public class Singleton {
    /**
    * 私有变量,外界无法访问
    * 可以定义 public 类型 instance变量,把属性直接暴露给客户对象,则没必要实现getInstance()方法
    * 但是可读性降低,而且直接暴露实例变量的名字给客户程序,会增加代码的耦合度
    */
    private static Singleton instance = new Singleton();
 
    static {
        //...
    }
 
    // 唯一的 private构造方法,客户对象无法创建该对象实例
    private Singleton() {
 
    }
 
    // 全局访问点
    public static Singleton getInstance() {
        return instance;
    }
}
 
// 客户使用单例模式代码
Singleton singleton = Singleton.getInstance();

如果该实例需要比较复杂的初始化过程时,把这个过程应该写在 static{ ... }代码快中。

注意:此实现是线程安全的,当对个线程同时去访问该类的  getInstance( ) 方法时,不会初始化多个不同的对象,这是因为,JVM 在加载此类时,对于 static 属性的初始化只能由一个线程执行且仅一次。

3、进阶:

    Statci 在加载类时就会被初始化,出于性能等方面的考虑,我们希望延迟实例化单例对象,只有在第一次使用该类的实例时才去实例化。

4、延迟创建 :

public static Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

我们把单例的实例化过程移至  getInstance( )方法,而不是在加载类时预先创建,当访问此方法时,首先判断该实例是不是已经被实例化过了,如果已被初始化,则直接返回这个对象的引用;否则,创建这个实例并初始化,最后返回这个对象引用。

使用  if (instance == null) 判断是否实例化完成了,此方法不是线程安全的。

编写高质量代码 改善Java程序的151个建议 PDF高清完整版 http://www.linuxidc.com/Linux/2014-06/103388.htm

Java 8简明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm

Java对象初始化顺序的简单验证 http://www.linuxidc.com/Linux/2014-02/96220.htm

Java对象值传递和对象传递的总结 http://www.linuxidc.com/Linux/2012-12/76692.htm

Java对象序列化ObjectOutputStream和ObjectInputStream示例 http://www.linuxidc.com/Linux/2012-08/68360.htm

本文永久更新链接地址http://www.linuxidc.com/Linux/2014-06/103535.htm

5、线程安全 :

在高并发的环境中,getInstance( ) 方法将返回多个指向不同的该类实例。

  Thread 1 Thread 2
1 if (instance == null)  
2   if (instance == null)
3 Singleton instance = new Singleton();  
4   Singleton instance = new Singleton();
5 return instance;  
6   return instance;

在时刻 1和 2,由于还没有创建单例对象,Thread 1 和 Thread 2都会进入创建单例实例的代码块分别创建实例。在时刻 3 ,Thread 1创建了一个实例对象,但是 Thread 2此时已无法知道,于是继续创建一个新的实例对象,导致这两个线程持有的实例并非为同一个。

更为糟糕的是,在没有自动内存回收机制的语言平台上运行这样的单例模式,如:C++,以为我们认为创建了一个单例实例,忽略了其他线程所产生的对象,不会手动去回收它们,从而引起内存泄漏。

为了解决这个问题,我们给次方法添加 关键字,代码如下:

public  static  synchronized  Singleton getInstance() {
    if  ( instance  ==  null ) {
        instance  =  new  Singleton();
    }
    return  instance ;
}

这样,再多的线程访问都只会实例化一个单例对象,实现了多线程的安全访问,但是在多线程高并发访问的情况下,给此方法加上 synchronized 关键字会是得性能大不如前。

更多详情见请继续阅读下一页的精彩内容http://www.linuxidc.com/Linux/2014-06/103542p2.htm

linux
相关资讯       Java单例模式 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款