单例模式.docx
《单例模式.docx》由会员分享,可在线阅读,更多相关《单例模式.docx(41页珍藏版)》请在冰豆网上搜索。
单例模式
Java单例模式详解
概念:
java中单例模式是一种常见的设计模式,单例模式分三种:
懒汉式单例、饿汉式单例、登记式单例三种。
单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
这些应用都或多或少具有资源管理器的功能。
每台计算机可以有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机中。
每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
总之,选择单例模式就是为了避免不一致状态,避免政出多头。
首先看一个经典的单例实现。
publicclassSingleton{
privatestaticSingletonuniqueInstance=null;
privateSingleton(){
//Existsonlytodefeatinstantiation.
}
publicstaticSingletongetInstance(){
if(uniqueInstance==null){
uniqueInstance=newSingleton();
}
returnuniqueInstance;
}
//Othermethods...
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。
此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。
)
但是以上实现没有考虑线程安全问题。
所谓线程安全是指:
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。
如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:
一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。
1publicclassTestStream{
2privateStringname;
3publicStringgetName(){
4returnname;
5}
6publicvoidsetName(Stringname){
7this.name=name;
8}
9//该类只能有一个实例
10privateTestStream(){}//私有无参构造方法
11//该类必须自行创建
12//有2种方式
13/*privatestaticfinalTestStreamts=newTestStream();*/
14privatestaticTestStreamts1=null;
15//这个类必须自动向整个系统提供这个实例对象
16publicstaticTestStreamgetTest(){
17if(ts1==null){
18ts1=newTestStream();
19}
20returnts1;
21}
22publicvoidgetInfo(){
23System.out.println("outputmessage"+name);
24}
25}
1publicclassTestMain{
2publicstaticvoidmain(String[]args){
3TestStreams=TestStream.getTest();
4s.setName("张孝祥");
5System.out.println(s.getName());
6TestStreams1=TestStream.getTest();
7s1.setName("张孝祥");
8System.out.println(s1.getName());
9s.getInfo();
10s1.getInfo();
11if(s==s1){
12System.out.println("创建的是同一个实例");
13}elseif(s!
=s1){
14System.out.println("创建的不是同一个实例");
15}else{
16System.out.println("applicationerror");
17}
18}
19}
运行结果:
张孝祥
张孝祥
outputmessage张孝祥
outputmessage张孝祥
创建的是同一个实例
结论:
由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
1.饿汉式单例类
1//饿汉式单例类.在类初始化时,已经自行实例化
2publicclassSingleton1{
3//私有的默认构造子
4privateSingleton1(){}
5//已经自行实例化
6privatestaticfinalSingleton1single=newSingleton1();
7//静态工厂方法
8publicstaticSingleton1getInstance(){
9returnsingle;
10}
11}
2.懒汉式单例类
1//懒汉式单例类.在第一次调用的时候实例化
2publicclassSingleton2{
3//私有的默认构造子
4privateSingleton2(){}
5//注意,这里没有final
6privatestaticSingleton2single=null;
7//静态工厂方法
8publicsynchronizedstaticSingleton2getInstance(){
9if(single==null){
10single=newSingleton2();
11}
12returnsingle;
13}
14}
3.登记式单例类
1importjava.util.HashMap;
2importjava.util.Map;
3//登记式单例类.
4//类似Spring里面的方法,将类名注册,下次从里面直接获取。
5publicclassSingleton3{
6privatestaticMapmap=newHashMap();
7static{
8Singleton3single=newSingleton3();
9map.put(single.getClass().getName(),single);
10}
11//保护的默认构造子
12protectedSingleton3(){}
13//静态工厂方法,返还此类惟一的实例
14publicstaticSingleton3getInstance(Stringname){
15if(name==null){
16name=Singleton3.class.getName();
17System.out.println("name==null"+"--->name="+name);
18}
19if(map.get(name)==null){
20try{
21map.put(name,(Singleton3)Class.forName(name).newInstance());
22}catch(InstantiationExceptione){
23e.printStackTrace();
24}catch(IllegalAccessExceptione){
25e.printStackTrace();
26}catch(ClassNotFoundExceptione){
27e.printStackTrace();
28}
29}
30returnmap.get(name);
31}
32//一个示意性的商业方法
33publicStringabout(){
34return"Hello,IamRegSingleton.";
35}
36publicstaticvoidmain(String[]args){
37Singleton3single3=Singleton3.getInstance(null);
38System.out.println(single3.about());
39}
40}
评论列表
#1楼2012-12-0922:
19佛心道骨
我感觉第一段不够有说服力,改成:
?
1
2
3
4
5
TestStreams=TestStream.getTest();
s.setName("张孝祥");
System.out.println(s.getName());
TestStreams1=TestStream.getTest();
s1.setName("非张孝祥");
更有说服力。
支持
(1)反对(0)
#4楼2013-03-1413:
26飝兒
登记式单例类的例子有些问题,比如:
classAextendsSingleton3{
publicstaticvoidmain(String[]args){
Singleton3s1=newSingleton3();
Singleton3s2=newSingleton3();
System.out.println(s1==s2);
}
}
第二份示例:
Java单例模式探讨
以下是我再次研究单例(java单例模式缺点)时在网上收集的资料,相信你们看完就对单例完全掌握了
Java单例模式应该是看起来以及用起来简单的一种设计模式,但是就实现方式以及原理来说,也并不浅显哦。
总结一下我所知道的单例模式实现方式:
1.预先加载法
Java代码
1.classS1{
2.privateS1(){
3.System.out.println("ok1");
4.}
5.
6.
7.privatestaticS1instance=newS1();
8.
9.publicstaticS1getInstance(){
10.returninstance;
11.}
12.}
[java]viewplaincopyprint?
1.classS1{
2.privateS1(){
3.System.out.println("ok1");
4.}
5.
6.
7.privatestaticS1instance=newS1();
8.
9.publicstaticS1getInstance(){
10.returninstance;
11.}
12.}
classS1{
privateS1(){
System.out.println("ok1");
}
privatestaticS1instance=newS1();
publicstaticS1getInstance(){
returninstance;
}
}
优点:
1.线程安全的
2.在类加载的同时已经创建好一个静态对象,调用时反应速度快。
缺点:
资源利用效率不高,可能getInstance永远不会执行到,但是执行了该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化了
2.initializationondemand,延迟加载法(考虑多线程)
Java代码
1.classS2{
2.privateS2(){
3.System.out.println("ok2");
4.}
5.
6.privatestaticS2instance=null;
7.
8.publicstaticsynchronizedS2getInstance(){
9.if(instance==null)instance=newS2();
10.returninstance;
11.}
12.}
[java]viewplaincopyprint?
1.classS2{
2.privateS2(){
3.System.out.println("ok2");
4.}
5.
6.privatestaticS2instance=null;
7.
8.publicstaticsynchronizedS2getInstance(){
9.if(instance==null)instance=newS2();
10.returninstance;
11.}
12.}
classS2{
privateS2(){
System.out.println("ok2");
}
privatestaticS2instance=null;
publicstaticsynchronizedS2getInstance(){
if(instance==null)instance=newS2();
returninstance;
}
}
优点:
资源利用率高,不执行getInstance就不会被实例,可以执行该类其他静态方法。
缺点:
第一次加载时发应不快,多线程使用不必要的同步开销大
3.initializationondemanddoublecheck双重检测(考虑多线程)
Java代码
1.classS3{
2.privateS3(){
3.System.out.println("ok3");
4.}
5.
6.privatestaticS3instance=null;
7.
8.publicstaticS3getInstance(){
9.if(instance==null){
10.synchronized(S3.class){
11.if(instance==null)
12.instance=newS3();
13.}
14.}
15.returninstance;
16.}
17.}
[java]viewplaincopyprint?
1.classS3{
2.privateS3(){
3.System.out.println("ok3");
4.}
5.
6.privatestaticS3instance=null;
7.
8.publicstaticS3getInstance(){
9.if(instance==null){
10.synchronized(S3.class){
11.if(instance==null)
12.instance=newS3();
13.}
14.}
15.returninstance;
16.}
17.}
classS3{
privateS3(){
System.out.println("ok3");
}
privatestaticS3instance=null;
publicstaticS3getInstance(){
if(instance==null){
synchronized(S3.class){
if(instance==null)
instance=newS3();
}
}
returninstance;
}
}
优点:
资源利用率高,不执行getInstance就不会被实例,可以执行该类其他静态方法。
缺点:
第一次加载时发应不快,由于java内存模型一些原因偶尔会失败
4.initializationondemandholder(考虑多线程)
Java代码
1.classS4{
2.privateS4(){
3.System.out.println("ok4");
4.}
5.
6.privatestaticclassS4Holder{
7.staticS4instance=newS4();
8.}
9.
10.
11.publicstaticS4getInstance(){
12.returnS4Holder.instance;
13.}
14.}
[java]viewplaincopyprint?
1.classS4{
2.privateS4(){
3.System.out.println("ok4");
4.}
5.
6.privatestaticclassS4Holder{
7.staticS4instance=newS4();
8.}
9.
10.
11.publicstaticS4getInstance(){
12.returnS4Holder.instance;
13.}
14.}
classS4{
privateS4(){
System.out.println("ok4");
}
privatestaticclassS4Holder{
staticS4instance=newS4();
}
publicstaticS4getInstance(){
returnS4Holder.instance;
}
}
优点:
资源利用率高,不执行getInstance就不会被实例,可以执行该类其他静态方法。
缺点:
第一次加载时发应不快
总结:
一般采用1即可,若对资源十分在意也可考虑4,不要使用2,3了。
测试代码:
(暂不探讨Class.forName类加载机制)
Java代码
1./**
2.*CreatedbyIntelliJIDEA.
3.*User:
yiminghe
4.*Date:
2009-6-8
5.*Time:
19:
20:
52
6.*/
7.publicclassSingleton{
8.publicstaticvoidmain(String[]args)throwsException{
9.System.out.println(Class.forName("S1"));
10.System.out.println(Class.forName("S2"));
11.System.out.println(Class.forName("S3"));
12.System.out.println(Class.forName("S4"));
13.}
14.}
15.
16./*
17.预先加载法
18.优点:
1.线程安全的,
19.2.在类加载的同时已经创建好一个静态对象,调用时反应速度快。
20.
21.缺点:
资源利用效率不高,可能这个单例不会需要使用也被系统加载
22.*/
23.classS1{
24.privateS1(){
25.System.out.println("ok1");
26.}
27.
28.
29.privatestaticS1instance=newS1();
30.
31.publicstaticS1getInstance(){
32.returninstance;
33.}
34.}
35.
36./*
37.initializationondemand,延迟加载法(考虑多线程)
38.优点:
1.资源利用率高
39.缺点:
第一次加载是发应不快,多线程使用不必要的同步开销大
40.
41.*/
42.classS2{
43.privateS2(){
44.System.out.println("ok2");
45.}
46.
47.privatestaticS2instance=null;
48.
49.publicstaticsynchronizedS2getInstance(){
50.if(instance==null)instance=newS2();
51.returninstance;
52.}
53.}
54.
55.
56./*
57.initializationondemand-doublecheck延迟加载法改进之双重检测(考虑多线程)
58.优点:
1.资源利用率高