2单例模式Word文档下载推荐.docx
- 文档编号:20280902
- 上传时间:2023-01-21
- 格式:DOCX
- 页数:36
- 大小:48.77KB
2单例模式Word文档下载推荐.docx
《2单例模式Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《2单例模式Word文档下载推荐.docx(36页珍藏版)》请在冰豆网上搜索。
2.
private
static
instance
=
null;
3.
4.
protected
ClassicSingleton()
5.
//
Exists
only
to
defeat
instantiation.
6.
}
7.
public
getInstance()
8.
if(instance
==
null)
9.
new
ClassicSingleton();
10.
11.
return
instance;
12.
13.}
publicclassClassicSingleton{
privatestaticClassicSingletoninstance=null;
protectedClassicSingleton(){
//Existsonlytodefeatinstantiation.
}
publicstaticClassicSingletongetInstance(){
if(instance==null){
instance=newClassicSingleton();
returninstance;
}
在例1中的单例模式的实现很容易理解。
ClassicSingleton类保持了一个对单独的单例实例的静态引用,并且从静态方法getInstance()中返回那个引用。
关于ClassicSingleton类,有几个让我们感兴趣的地方。
首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;
结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。
这种技巧可以确保单例类的实例只有在需要时才被建立出来。
其次,注意ClassicSingleton实现了一个protected的构造方法,这样客户端不能直接实例化一个ClassicSingleton类的实例。
然而,你会惊奇的发现下面的代码完全合法:
SingletonInstantiator
SingletonInstantiator()
ClassicSingleton.getInstance();
4.ClassicSingleton
anotherInstance
5.new
...
8.}
publicclassSingletonInstantiator{
publicSingletonInstantiator(){
ClassicSingletoninstance=ClassicSingleton.getInstance();
ClassicSingletonanotherInstance=
newClassicSingleton();
...
前面这个代码片段为何能在没有继承ClassicSingleton并且ClassicSingleton类的构造方法是protected的情况下创建其实例?
答案是protected的构造方法可以被其子类以及在同一个包中的其它类调用。
因为ClassicSingleton和SingletonInstantiator位于相同的包(缺省的包),所以SingletonInstantiator方法能创建ClasicSingleton的实例。
这种情况下有两种解决方案:
一是你可以使ClassicSingleton的构造方法变化私有的(private)这样只有ClassicSingleton的方法能调用它;
然而这也意味着ClassicSingleton不能有子类。
有时这是一种很合意的解决方法,如果确实如此,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些性能优化选项。
另一种解决方法是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。
关于ClassicSingleton的第三点感兴趣的地方是,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。
假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
第四点,如果ClasicSingleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。
不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
最后也许是最重要的一点,就是例1中的ClassicSingleton类不是线程安全的。
如果两个线程,我们称它们为线程1和线程2,在同一时间调用ClassicSingleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有ClassicSingleton的两个的实例被创建。
正如你从前面的讨论中所看到的,尽管单例模式是最简单的设计模式之一,在Java中实现它也是决非想象的那么简单。
这篇文章接下来会揭示Java规范对单例模式进行的考虑,但是首先让我们近水楼台的看看你如何才能测试你的单例类。
测试单例模式
接下来,我使用与log4j相对应的JUnit来测试单例类,它会贯穿在这篇文章余下的部分。
如果你对JUnit或log4j不很熟悉,请参考相关资源。
例2是一个用JUnit测试例1的单例模式的案例:
例2.一个单例模式的案例
1.import
org.apache.log4j.Logger;
2.import
junit.framework.Assert;
3.import
junit.framework.TestCase;
5.public
SingletonTest
extends
TestCase
sone
null,
stwo
Logger
logger
Logger.getRootLogger();
SingletonTest(String
name)
super(name);
void
setUp()
13.
logger.info("
getting
singleton..."
);
14.
15.
...got
singleton:
"
+
sone);
16.
17.
18.
19.
stwo);
20.
21.
testUnique()
22.
checking
singletons
for
equality"
23.
Assert.assertEquals(true,
24.
25.}
importorg.apache.log4j.Logger;
importjunit.framework.Assert;
importjunit.framework.TestCase;
publicclassSingletonTestextendsTestCase{
privateClassicSingletonsone=null,stwo=null;
privatestaticLoggerlogger=Logger.getRootLogger();
publicSingletonTest(Stringname){
super(name);
publicvoidsetUp(){
logger.info("
gettingsingleton..."
sone=ClassicSingleton.getInstance();
...gotsingleton:
"
+sone);
stwo=ClassicSingleton.getInstance();
+stwo);
publicvoidtestUnique(){
checkingsingletonsforequality"
Assert.assertEquals(true,sone==stwo);
例2两次调用ClassicSingleton.getInstance(),并且把返回的引用存储在成员变量中。
方法testUnique()会检查这些引用看它们是否相同。
例3是这个测试案例的输出:
例3.是这个测试案例的输出
1.Buildfile:
build.xml
3.init:
[echo]
Build
20030414
(14-04-2003
03:
08)
6.compile:
8.run-test-text:
[java]
.INFO
main:
[b]getting
singleton...[/b]
INFO
[b]created
[/b]
Singleton@e86f41
equality
Time:
0.032
OK
(1
test)
Buildfile:
build.xml
init:
[echo]Build20030414(14-04-200303:
08)
compile:
run-test-text:
[java].INFOmain:
[b]gettingsingleton...[/b]
[java]INFOmain:
[b]createdsingleton:
[/b]Singleton@e86f41
...gotsingleton:
Singleton@e86f41
checkingsingletonsforequality
[java]Time:
0.032
[java]OK(1test)
正如前面的清单所示,例2的简单测试顺利通过----通过ClassicSingleton.getInstance()获得的两个单例类的引用确实相同;
然而,你要知道这些引用是在单线程中得到的。
下面的部分着重于用多线程测试单例类。
多线程因素的考虑
在例1中的ClassicSingleton.getInstance()方法由于下面的代码而不是线程安全的:
1.1:
2.2:
Singleton();
3.3:
1:
2:
instance=newSingleton();
3:
如果一个线程在第二行的赋值语句发生之前切换,那么成员变量instance仍然是null,然后另一个线程可能接下来进入到if块中。
在这种情况下,两个不同的单例类实例就被创建。
不幸的是这种假定很少发生,这样这种假定也很难在测试期间出现(译注:
在这可能是作者对很少出现这种情况而导致无法测试从而使人们放松警惕而感到叹惜)。
为了演示这个线程轮换,我得重新实现例1中的那个类。
例4就是修订后的单例类:
例4.人为安排的方式
3.public
Singleton
singleton
boolean
firstThread
true;
Singleton()
if(singleton
simulateRandomActivity();
created
singleton);
singleton;
simulateRandomActivity()
try
if(firstThread)
false;
sleeping..."
25.
This
nap
should
give
the
second
thread
enough
time
26.
get
by
first
thread.
27.
Thread.currentThread().sleep(50);
28.
29.
30.
catch(InterruptedException
ex)
31.
logger.warn("
Sleep
interrupted"
32.
33.
34.}
publicclassSingleton{
privatestaticSingletonsingleton=null;
privatestaticbooleanfirstThread=true;
protectedSingleton(){
publicstaticSingletongetInstance(){
if(singleton==null){
simulateRandomActivity();
singleton=newSingleton();
createdsingleton:
+singleton);
returnsingleton;
privatestaticvoidsimulateRandomActivity(){
try{
if(firstThread){
firstThread=false;
//Thisnapshouldgivethesecondthreadenoughtime
//togetbythefirstthread.
Thread.currentThread().sleep(50);
catch(InterruptedExceptionex){
logger.warn("
Sleepinterrupted"
除了在这个清单中的单例类强制使用了一个多线程错误处理,例4类似于例1中的单例类。
在getInstance()方法第一次被调用时,调用这个方法的线程会休眠50毫秒以便另外的线程也有时间调用getInstance()并创建一个新的单例类实例。
当休眠的线程觉醒时,它也会创建一个新的单例类实例,这样我们就有两个单例类实例。
尽管例4是人为如此的,但它却模拟了第一个线程调用了getInstance()并在没有完成时被切换的真实情形。
例5测试了例4的单例类:
例5.失败的测试
throws
InterruptedException
Both
threads
ca
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 模式