|
Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
0 K G; Q8 ^! @6 I/ P" V5 D6 A6 H$ W - *
/ ?! [4 g7 C1 Z- F5 j# Y! g8 v- A - * 单例模式的实现:饿汉式,线程安全 但效率比较低
8 e6 k3 g* R( R) L1 i0 o- X8 v0 i! j - */
" W. E: t+ Y( P7 G; J c- o - public class SingletonTest { T3 @& C. Y1 p6 A/ c
- 6 ^9 T& g" s' Q/ c4 L% W3 f
- private SingletonTest() {
9 X9 n& M8 Q% ? - }
% P6 ]- ^$ `1 ~2 b8 o" c - - E, u. J1 Z w9 q! a
- private static final SingletonTest instance = new SingletonTest(); ! Y. [& ~) s, T
- 4 \& u& b4 E* B
- public static SingletonTest getInstancei() {
Y& G% Y2 Y! e* P1 e2 Z - return instance; 5 d7 e4 i% r3 c6 ~; c$ H
- }
* d. P8 V6 Q4 m+ Q; }, d) ?4 H -
2 t$ ?0 u3 b j# \' q, g2 @0 ? - } </font></font>
复制代码 (2)非线程安全
7 @+ j; u2 e* x1 {* u E- <font color="#454545"><font face="Verdana,">/** ; Y5 ~1 Y! _! L7 {1 q$ M* w5 _
- * 单例模式的实现:饱汉式,非线程安全
5 G5 p2 J# j, p# Q" y6 V* Y; ^7 Z - *
8 b4 o, F+ f1 t8 [6 o* F# ` - */
\& w" j! N0 H6 H. } - public class SingletonTest { * H/ b- q0 @0 h9 O- G
- private SingletonTest() { 5 q2 A T4 W* c: _+ D J# _
- } . R" C( w& R" `$ z: v
- Q4 I0 U, p! B I7 e9 z! g
- private static SingletonTest instance; + {) o$ O' f4 o4 z# Z
-
& U3 Z: B. X' x/ h( f. N6 i - public static SingletonTest getInstance() {
+ v) T6 j9 @# H - if (instance == null) 0 k0 C+ M) I$ a; w1 L' G5 p
- instance = new SingletonTest();
7 z& w }. q! D6 P/ c - return instance;
8 t8 l0 |/ v* C - } + d. s0 \4 J1 N5 j8 x( r2 x- T
- }</font></font>
复制代码
8 f9 A# U1 G( Y# k8 @: |9 o(3)线程安全,但效率比较低
0 `; {- S, q$ [2 c" ~* ]4 [, M- <font color="#454545"><font face="Verdana,">/**
7 N G! r2 q4 J - * 线程安全,但是效率非常低
$ R, a# L0 }* e. d$ E0 M4 W7 o' { - * @author vanceinfo
1 D7 [ ]- Z6 c7 N' L% F7 r - * " {( J/ C. _. y$ G, j- l
- */
' L4 o4 N: e% D" H$ j) @ - public class SingletonTest {
- f2 I3 r4 [* M. X2 } - private SingletonTest() { ; H4 X4 A2 K6 C2 x# c0 c( F
- }
" v+ T1 a, }% t+ \; C -
% c% U* f! B6 [# c - private static SingletonTest instance; # W2 f Y- A/ P4 Z3 ~% s
-
# G: r# h6 I9 q9 i3 @! N - public static synchronized SingletonTest getInstance() { 4 n' h% Y+ @; }) q* P
- if (instance == null) 0 b' Q, c5 J. b9 X' B$ @" m
- instance = new SingletonTest();
# @4 p2 x: S. S! f: t' n - return instance; " q# ^1 f6 J% v; f# O; r" X
- }
$ S5 l- O* A* c& I7 G5 u. z3 g" o - }</font></font>
复制代码 (4)线程安全,并且效率高( k0 M" P( F. R7 g' t
- <font color="#454545"><font face="Verdana,">/** 9 f$ b# z) H1 @$ @ D) `
- * 线程安全 并且效率高 ( s3 I7 e h8 ^( O- z
- * 5 d, Z, \/ m: v6 [3 [3 r$ k
- */
: A5 {7 P1 a/ t: |5 t - public class SingletonTest {
, Q+ W1 F8 t. B$ b' r1 L. x+ { - private static SingletonTest instance; / t* J* |' B# n. C: k: h; _
- 0 Z1 G+ Q5 m1 H* ^/ m. A! w
- private SingletonTest() { + S7 _0 W, S2 N
- } $ ?! W# b# S2 N
-
- c6 o" d5 }. @/ D( \5 U - public static SingletonTest getIstance() {
* U, ?& J7 d3 I - if (instance == null) { # o0 l( N( c- A' Z8 D7 F
- synchronized (SingletonTest.class) { 6 E0 C$ s. K, X8 {% Z6 V1 O# A$ C! v+ b
- if (instance == null) {
/ ] b/ _. Z4 r, c5 X - instance = new SingletonTest(); " ?$ G5 ]& Y. p; M4 L
- }
, b- G- R' w1 v - } . u# D( j# c" x+ @
- } & T9 o( t: h3 r) r
- return instance;
/ Y' i( W* e5 J E/ T - }
1 w/ E' M* y% p: \: C0 K/ H4 t - }</font></font>
复制代码 (5)Lazy initialization holder class模式/ R& H5 F+ {$ V" S
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
8 n0 ^$ h* \( h/ ]+ y; X1:先来看点相应的基础知识
+ V0 k) o/ s6 l 先简单的看看类级内部类相关的知识。 - 什么是类级内部类?
( Z: C- d7 i: I! [! p6 q 简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载
$ M- [7 h5 \- A, P- h
再来看看多线程缺省同步锁的知识。% e) Y% F- M Y" i( F
大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时2 N* l! @, p H* R! z
2:接下来看看这种解决方案的思路# `9 Z' _( _/ m- t- y6 t; D9 }% F/ V6 y
要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。 Z: d R$ j; N) r1 F
如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。1 T( N* r- d3 |" i
看看代码示例可能会更清晰,示例代码如下: - public class Singleton {
5 q7 d: p: g& a! T( { - /**3 G" P7 P' ^! K m5 k
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例+ o' k: E2 h; q5 Q: b0 W7 q# p2 o
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载4 O5 w4 w7 a" g/ _3 N; T
- */
5 O9 p# z& v4 ^8 G7 q$ Q8 \ - private static class SingletonHolder{
, b6 R' c9 O- s' R- h8 {6 c - /**
$ \( G9 [- f% [- l( J; v - * 静态初始化器,由JVM来保证线程安全( X5 |: S$ a( T" Y% ?7 U
- */6 p+ q3 x. u8 ~) x$ F$ a5 A
- private static Singleton instance = new Singleton();
; a5 ^! }/ r" X2 Z4 c - }4 H6 \$ K# t3 M
- /*** _5 R' c; @: v) b2 c- q
- * 私有化构造方法1 b' n1 I/ ^) ?1 w" `
- */) T" s& d: f/ Z. y w0 Y/ z: f& w
- private Singleton(){! A3 v5 r5 T' B/ X6 ]
- }
# F9 H, @: O6 s5 ~ - public static Singleton getInstance(){( s+ `( Y* P' c$ S
- return SingletonHolder.instance;
& b3 d1 _/ z! _1 ^. o1 [ - }& e! k; a9 f, Z
- }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。4 a1 ]$ C$ F4 h
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。 B0 \, h; {; @# x) _$ f
总结:
) Q9 y) G$ I3 v
1 p# u5 X @0 R1 X0 W& A单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
2 ?' \) C5 p) @ / m2 j4 y$ e$ Q7 ?% q& l
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。 V8 @) F0 U+ U3 w
7 d, n% z# m2 Q; b
单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。
. U( g9 z9 [/ r6 P* A5 }& n. v# V, d& F$ f* ~( }0 k/ N& f
- C7 W6 t2 i( Q' B* Y. z3 }& M
/ B ]' [* z" a
* e+ ~+ J+ z( {* l: z6 b
0 c6 t f/ F/ C0 p2 s% M
|