Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/** 6 d+ N* H: U8 {
- * , z% I3 W# t1 r) j( U e! M/ q% b
- * 单例模式的实现:饿汉式,线程安全 但效率比较低
0 s* t8 X3 Q; t" R' F - */
+ Z$ v. J. y( Q! o, p- d - public class SingletonTest {
l, |" K8 m; S% j. C; j; a! N -
: L0 o6 L& d8 ?# Y+ F# A - private SingletonTest() { 8 x; J0 r. `% E
- } + m7 Q& K4 s! N9 m# l# n5 ~" A0 l. Z
- 4 [0 ?# h' N" O% x; g4 ~
- private static final SingletonTest instance = new SingletonTest();
) t+ ?1 Q4 m8 A. w: H -
) t7 u. q4 l0 B: u - public static SingletonTest getInstancei() { , c7 m5 [; T5 j, Q5 T1 N; \
- return instance; ' t: F5 I: s9 t
- }
, i6 j7 v& b7 s9 m1 v) Q -
( O2 w s- @- s - } </font></font>
复制代码 (2)非线程安全
?0 {: p2 U0 x, }- B: X- <font color="#454545"><font face="Verdana,">/** - F; U, L' C) D: Z" n6 |
- * 单例模式的实现:饱汉式,非线程安全 6 m0 U: H' q( Q/ v
- *
' K% T% i4 Q' U: u4 Q - */
8 {, s3 q, c+ ?( P. w$ e+ t9 B - public class SingletonTest {
+ d: U* h% I* F7 b3 t - private SingletonTest() { " I( ?, L' h v u& f! x
- } $ s" h" M5 X' \" J$ I1 Q
-
" S1 r1 w; S( e; ` - private static SingletonTest instance; 8 Q% l% o0 t* E) o! {. i
-
+ r9 R' ?' w! u) a( \* C: H2 z - public static SingletonTest getInstance() {
, e# t: S+ d# D1 g- S, {% w - if (instance == null) $ I$ [. \ h% ~
- instance = new SingletonTest(); + c: q3 X6 _1 J$ O: X! x" G `
- return instance;
/ }. _ A: o( p/ @# s. J5 ^ J - }
6 `+ ], J1 V# ]7 a5 ]' b - }</font></font>
复制代码
4 N0 N: u6 i; }5 @2 K(3)线程安全,但效率比较低
/ P& [3 [' J7 Z1 u! X- <font color="#454545"><font face="Verdana,">/**
) {$ k6 q6 J" ?' Z - * 线程安全,但是效率非常低
1 _8 x% T# L: z - * @author vanceinfo
* _3 Q! `9 W4 w' v - * 3 F3 ]! A' [5 ^7 @9 A; _3 _. x
- */ 9 j. t8 ?* }3 v1 t. ^
- public class SingletonTest { % x# ^" }! m$ d$ P8 T. D
- private SingletonTest() {
% S6 ^6 O0 [7 z1 X0 j7 z - }
9 x3 K; ^4 L1 @0 _( p - 8 b) J5 H8 d' l9 u3 C. D
- private static SingletonTest instance;
$ o! O6 K: X6 T( X3 P$ }" T0 v7 M R( d -
; n) R8 j# s6 a& g: {; G - public static synchronized SingletonTest getInstance() {
) k6 P+ |. B# H4 z+ M3 x - if (instance == null) ! ~7 m, j# a5 s: i+ Y2 w
- instance = new SingletonTest(); % T. Z9 e* P4 H% r
- return instance; ( ^, c7 E* ~8 J; C
- } & w, t+ P% R" ?2 @- `
- }</font></font>
复制代码 (4)线程安全,并且效率高$ o' q4 U" O( W
- <font color="#454545"><font face="Verdana,">/** ' Y% C7 K/ F* E2 n! E* f {
- * 线程安全 并且效率高 ) x& y# G/ ?0 N q. b2 R$ Z
- *
# f# X" Q* Q7 u% D - */ ' ~* H5 }* l' w# |* S: I
- public class SingletonTest {
g3 D/ k4 N) P1 L - private static SingletonTest instance;
p, R' V' H' [ -
% x+ l) ^+ J3 H - private SingletonTest() { 1 U8 R$ n- p$ c. b' X
- } J7 o$ O3 q# K/ G1 h5 ]
-
: O6 e( s* ^8 M7 A+ g- T - public static SingletonTest getIstance() { 9 m$ b) T' m1 K9 z
- if (instance == null) {
8 {! w* D, h* J3 b4 H0 O - synchronized (SingletonTest.class) { ' Q# _& a/ c( I& C8 b6 d
- if (instance == null) { 9 Z3 Y9 ^* l) x- t6 J9 G: @% A# V
- instance = new SingletonTest();
A' m7 }7 ?+ u5 p - } - D! a6 ^ e) X2 d4 K% ^' b
- } 4 ~+ b* C F* W- B
- } : i) N; b3 e% S0 I6 Y0 z
- return instance;
- X; c% A9 J/ C. X1 I" B+ N - }
0 f8 T+ Y. ~0 p: V9 n4 Q7 E& G - }</font></font>
复制代码 (5)Lazy initialization holder class模式
# k& e" J5 V [ x+ j6 pLazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
/ C2 Q8 ]' R# @* I5 P$ n: v4 B1:先来看点相应的基础知识6 K2 [5 a. o& B
先简单的看看类级内部类相关的知识。 - 什么是类级内部类?
" E8 C5 W6 ]+ }1 F 简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载$ b" o9 Y% a( E6 u1 h/ e
再来看看多线程缺省同步锁的知识。
L) q( J% J7 y4 F 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时4 D% G# ^' h* B0 F
2:接下来看看这种解决方案的思路 W/ p2 v8 e5 h8 D9 b/ _" b
要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。
# W6 L4 {' k: u4 ~. p 如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。
8 x% k6 @; O" H: _) g& k 看看代码示例可能会更清晰,示例代码如下: - public class Singleton {4 ? j+ w% U6 `$ a6 e
- /**' \& t' |" l E8 e+ g
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例3 u/ K, M% I! m' v! b
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载' d/ A8 P. g9 O3 `1 P
- */7 b B- f' |1 ~- t7 B% \' ]! [& r. P
- private static class SingletonHolder{
; C3 V! @: ^3 ]0 @/ Q - /**' A" H- z* ?5 y9 ]1 s# m) K( K
- * 静态初始化器,由JVM来保证线程安全
- ^9 T9 R8 a) Q7 C" X* R - */2 B- t# a: _9 m+ W8 o
- private static Singleton instance = new Singleton();2 w6 ]8 _( W V) }
- }
; v" ~. n% ^6 B2 d6 N3 o - /**" @4 [% |( v. }
- * 私有化构造方法
) `" b& q# M" r0 l5 F% A3 W' f - */
6 z: F6 Z& ^6 Z% t, f5 j - private Singleton(){
$ d# o# P1 L9 E6 a7 t; T - }9 F6 X% O! R1 U0 L0 w9 n
- public static Singleton getInstance(){
' U$ y8 H( V8 r7 g7 h5 ?0 x - return SingletonHolder.instance;% Z, f& O6 Y8 c- g1 E
- }( E7 }% ~3 _& }! \
- }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。5 U' _9 |' ^9 t" d9 K4 ~6 r1 P
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
8 _4 o9 C) }" G% E3 a4 N总结: F+ ^8 i9 S$ n, y' v V
# n8 r& N9 {6 |& g: H
单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。* G8 ]8 W- X! S. c& G
5 @& o: N( i$ z" z
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。- x. U1 E$ x( G5 Q1 o, i$ D
+ k' f6 [' \0 H' J& a& V1 t
单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。
# X; j- H; T7 f; g$ f$ E4 R6 W/ f5 e: X k
# O! x% N' [( x* o5 s' u9 m3 D6 z* \: m7 @
+ S% I6 v9 D2 D' p
$ ?2 C5 V! L L4 f% }7 b K |