`
yangshen998
  • 浏览: 1246902 次
文章分类
社区版块
存档分类
最新评论

java 设计模式 学习笔记(16) 单例模式

 
阅读更多

单例模式:保证一个类仅有一个实例,并提供一个访问其实例的一个全局访问点

根据单例模式的定义,写一个单例模式的例子需要注意两点:

1.实例有该类自己生成

为了防止客户代码通过 new Singleton()来实例一个对象,需要将 Singleton的默认构造函数定义为private

2. 提供一个访问其实例的全局访问点

将访问方法getInstance()定义为 static ,则直接通过Singleton.getInstance() 来获取到实例

3.单例只能有一个实例

在Singleton类中,将instance 定义为 static ,则保证了不管多少个 Singleton都只有并仅有一个instance 静态变量。

单例的实现有两种方式:

饿汉单例:在类被加载时就初始化实例

懒汉单例:类加载之后,第一次调用getInstance()方法时,才初始化实例。

不过懒汉式单例的getInstance() 方法需要注意到线程的同步问题:在单例类已经被加载进来,但还没有被调用生成实例之前,有多个线程同时调用getInstance() 方法,那么则可能多次调用 instance = new Singelton_lazy()语句,导致出现多个实例的结果。

所以懒汉单例的getInstance() 方法要使用synchronized 来保证其线程安全性。

现在我们来模拟懒汉单例模式的getInstance() 如果不注意线程安全性时出现的线程不安全现象:

现在将Singleton_lazy 改变一下: 1. 去掉synchronized

2. 认为的改变getInstance() 方法,但传入的参数为0时,则访问SingletonFull的线程需要休眠500毫秒, 当500毫秒一过,则返回一个单例的实例

现在编写两个线程类:

编写测试用的代码:

输出结果:

从输出结果中,我们可以看到Mythread 和 Mythread2 两个线程先后访问getInstance()方法,Mythread 在访问getInstance ()时,因为带了参数 0, 所以getInstance()在还没有建立单例实例时停顿了500毫秒。而在这短时间里,Mythread2 趁虚而入,访问 getInstance()方法,立马生成一个单例实例,并返回给Mythread2 。之后500毫秒过了,Mythread所访问的getInstance 线程苏醒,继而执行 instance = new SingletonFull() 方法,返回另外一个单例实例。

那么换个思路,我是否可以将测试用的代码换成:

SingletonFull sFull = SingletonFull.getInstance(0);
SingletonFull sFull2 = SingletonFull.getInstance(1);
System.out.println(sFull);
System.out.println(sFull2);

结果显示只生成了一个单例实例,为什么呢?

前面提到了在SingletonFull 中的 Thread.currentThread.sleep(500),是使访问Singleton 类的线程休眠,而不是让SingletonFull 休眠,而且SingletonFull 不是线程类,无法休眠。

通过将getInstance() 修改成下面的代码,仅仅是添加了一个输出当前访问SingletonFull 类的线程名字。

接着改变测试代码,也添加了一条输出当前线程名称的代码

最后结果输出:

从结果中可以看到在整个测试代码运行过程中,只有main 线程在运行。并且这里,由于main线程先休眠了500毫秒,然后才依次打印出单例实例的名称。而在前面的测试代码中,程序一执行,就先输出了一个单例实例名称,过了500毫秒后,才输出另外一个单例实例名称。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics