Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
( j; j! h+ _4 C8 i1 {# W" O5 D+ K - * , \, ~5 v. K' Z: @
- * 单例模式的实现:饿汉式,线程安全 但效率比较低
. V1 y6 \0 s/ s% A - */ ' C( ~# q; f3 ?; o3 n: h2 u7 s& ~
- public class SingletonTest { 9 y5 o* B! c/ n
- 3 n1 a2 p# H3 Y- K7 g" W
- private SingletonTest() {
2 V6 |, C5 w, ?- |, Y; K - } 0 k: H1 y% I+ Z7 n8 Y
- 4 @3 U# ?: I* A/ ^- E
- private static final SingletonTest instance = new SingletonTest();
8 z+ ?& ?1 G" u9 m4 ^8 p - c& l1 \# c* o5 I2 Y+ [, q" a
- public static SingletonTest getInstancei() { 9 R" t+ B% R7 `! U% `# d
- return instance;
2 s2 K, [4 H% ], M7 H) t0 \/ k2 ] - } 7 W6 o9 i$ A) p4 ^# g
-
8 O2 m* q4 i1 G2 n# } J - } </font></font>
复制代码 (2)非线程安全
1 L% C6 t& P% t4 [- <font color="#454545"><font face="Verdana,">/**
2 F; S) t. i3 v/ J& [ - * 单例模式的实现:饱汉式,非线程安全 . m) t$ C3 x8 z7 l
- *
: X8 ~; s1 Y% ]; l9 S4 f - */ , H$ d9 j2 L; E- h
- public class SingletonTest {
* J8 m7 I1 ~2 n* O - private SingletonTest() { 8 Y8 `) ^' S& x% H
- }
0 ?5 o7 \7 d* _7 R) Z$ U8 V -
" r, R, X- {5 [ {" S/ h; ~ - private static SingletonTest instance;
7 Z& X( [: k$ f2 q4 m) @ - , w; r$ u& n' r) O9 E
- public static SingletonTest getInstance() {
% J$ N) ~! M; r" H* b! X, V" H: F4 y c - if (instance == null)
7 f% R8 W3 ^9 \, G) s1 O5 e - instance = new SingletonTest();
% f. y$ \7 G" i) N, e5 f - return instance; - E$ l# L# }. b" h- S( v! ?, D
- } ! J/ K, p) ` ]/ T1 R" H4 C
- }</font></font>
复制代码 ( h2 u! x8 H9 }) D
(3)线程安全,但效率比较低
) O- Z; K# G& L% ^" f- ^" L- <font color="#454545"><font face="Verdana,">/** : e) i5 P- K% q* [7 F% ]2 f
- * 线程安全,但是效率非常低 9 ]+ R$ n; c% K# K. c
- * @author vanceinfo
3 u3 H# l5 I9 B, ]# R& T- F - * + m6 [0 R( V0 C) s4 ], u6 y- m% |
- */ 8 N2 c6 e. w0 {* W8 X% y* d. S- U
- public class SingletonTest {
\/ K% _/ i+ g/ Y: s7 Y3 w! R4 F - private SingletonTest() {
3 Z! x5 v% p0 {* H - } x1 r( l0 P# X$ c, h
- 5 j6 Z: O, V! _: z5 p
- private static SingletonTest instance;
2 O" Y9 v$ s; T. Y - ; e5 y. [, z% }- N0 f# B3 V
- public static synchronized SingletonTest getInstance() {
: V V9 ?& \+ R+ X( x: | - if (instance == null) ) v3 f e% W1 v i
- instance = new SingletonTest(); ( |4 q' V0 \3 C. ?, d2 v
- return instance;
" h$ A) E* |. q/ o - }
7 a9 n: M# a @$ f& {: b+ a) N+ @ - }</font></font>
复制代码 (4)线程安全,并且效率高
S8 _6 D6 C) a- <font color="#454545"><font face="Verdana,">/** 8 b1 b3 U( d+ m% l1 o
- * 线程安全 并且效率高
+ o7 q" m" u" L3 z - *
2 |( A( J7 |- Y+ v, { - */
. f J$ n! d8 V- E' \4 X - public class SingletonTest { $ ?0 W1 k$ a' ~& A9 {% _4 g9 X
- private static SingletonTest instance;
/ B/ Y+ @0 L9 e2 p. R4 H - : w! c5 b/ H: Z9 f1 r9 y+ }
- private SingletonTest() { & Z q# l2 b1 m- W; k( S; A$ L
- } & \1 g; r% u2 _4 q6 _) Y
- ! z; h# i) G$ |" i5 u* {
- public static SingletonTest getIstance() {
8 i; N* {* c b" k - if (instance == null) {
& I7 V& ^0 `2 c3 @1 z, w7 ]# c* k - synchronized (SingletonTest.class) {
7 t% ~1 Y' g& Q( h1 {$ Q# q/ v - if (instance == null) { + y! ^* w$ O" V, i- z0 ]6 I" D. R
- instance = new SingletonTest();
' r# O/ [' c( P - }
9 w' N2 X* i# ~- \ - }
& Z. W( i1 g f9 ^+ v# ^ - } 5 E9 _* i" |, z- d. i2 z
- return instance; 3 {8 G* d/ E2 e; e0 B1 _
- } - Y( H" r& F! }. B. _1 ^% X# a
- }</font></font>
复制代码 (5)Lazy initialization holder class模式4 A8 d$ V# H/ ^7 g
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
3 k- G4 Z& P$ L1 S1 @0 A% U1:先来看点相应的基础知识2 e- J: @5 f8 V% K# }
先简单的看看类级内部类相关的知识。 - 什么是类级内部类? r! k) d, j0 ]: }
简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载
1 q% V3 Z0 I& a3 E. E5 \3 B
再来看看多线程缺省同步锁的知识。
. M* d1 P# `4 f1 ~$ P 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时
0 o( V# |( G$ {2 v( }
2:接下来看看这种解决方案的思路
2 x$ w! M B+ H 要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。
8 ?2 l' f6 ^9 ]. `# |$ d1 e+ Y 如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。
4 n8 V% Q3 s* l+ H. s5 t/ o 看看代码示例可能会更清晰,示例代码如下: - public class Singleton {
]9 Z' S0 W) e. d4 U - /**
% i! h+ g& H0 K [( l6 R' Z - * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例+ W) z0 O/ R$ F/ }7 g1 J/ [
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载. ^ u' } B2 `4 O" Z' K
- */
/ b$ U) d/ c( J1 |+ w5 K& L - private static class SingletonHolder{
. m/ ^2 ^* ^+ P1 c5 g - /**8 ^9 D; f6 ^( N
- * 静态初始化器,由JVM来保证线程安全
( L" p& U( x, @3 m - */( K7 _0 e8 b0 [6 G* a# ~- x
- private static Singleton instance = new Singleton();
4 a, E; W# b' ~8 C - }
5 r/ ~$ l* t- |5 d) a - /**
7 u9 x. |+ _8 i( @, b- ~( n - * 私有化构造方法/ N* ^& u' e: v" y" S* K
- */
3 O4 o; o# _( x6 N% q* p - private Singleton(){
$ x/ A* A$ Y" B' P - }. c+ l8 [( B; C, i
- public static Singleton getInstance(){ d) @, D+ y/ h; b& A
- return SingletonHolder.instance;: s0 f4 t( x4 w# _- |
- }, P( b2 }- s/ B7 r2 ~7 H5 m
- }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
5 I1 l0 v. Z7 X ?( N 这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
3 I6 @! @" n' u8 C" i总结:' ?. B3 ?" d! s, \; g' D9 U
( m" H" h' C; U k" p+ o; j8 Z' ]
单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
; J Q1 G: v1 | 3 O0 G% k/ i- V+ j
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。$ p4 T7 ] f8 b% e
3 J' C- X2 Y$ B* q; G单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。 1 `# p& T1 o% Z7 `( K- s. r
* e$ y& }/ b0 l! X7 g: [3 y7 q, ^& }" b5 v/ `
& n# p2 y, Y+ y$ K( C( |% e: |1 y
1 ^7 A9 _( x; ^0 F
|