Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
2 x9 E" _: J+ Z' |- E! G - * ( B/ C, L: v3 i6 ~
- * 单例模式的实现:饿汉式,线程安全 但效率比较低 3 v2 F+ k: z" r0 r" p
- */ 9 S$ T# C, B1 @4 R$ e. z& t
- public class SingletonTest { * g o p7 X) H: w* F/ F& ^% U" C l
- : K3 W8 a% I. {) u7 h. ]- \" l# j& E
- private SingletonTest() { ( U# q4 _- Y: I4 K
- } : p" }2 E* v& n; I' ^
- : R- J' N' K3 U0 C' T5 ~8 Q
- private static final SingletonTest instance = new SingletonTest(); 5 w. S0 Q6 @/ i0 D ^( a4 X, b
-
) _! ^! x6 U5 l - public static SingletonTest getInstancei() {
$ |- T+ \: s8 N2 k! e9 ]0 ^- w6 p - return instance;
, R8 J+ U* W. I$ a$ ^$ t3 W. A - }
0 j+ Q' @$ U, t% T, r - & ^8 Z6 X% X# `, D
- } </font></font>
复制代码 (2)非线程安全
; v- b+ q a6 `" x i8 ^8 b- <font color="#454545"><font face="Verdana,">/** 9 K# G; U" ]( c% V
- * 单例模式的实现:饱汉式,非线程安全 1 F3 v% h9 W' |) r% ^' O- S8 O
- * - y" M Z* ` B% M( R7 d
- */
5 _9 E1 R P3 ]. I; E x1 X- V - public class SingletonTest { ! ^, ?( v- y) n$ ^" s D$ } e
- private SingletonTest() {
2 @" j# D P4 I: y. ]. Z# _9 C - }
/ G @0 m, p8 b% W* T: V+ [- Q4 v - U$ `# Y' `' s. Q3 z. ]! e! E
- private static SingletonTest instance;
4 J) Y+ q e9 Z+ W2 i8 m - ; B. I* e/ @2 v g. T) x
- public static SingletonTest getInstance() { 5 F5 n8 M' \: j$ B4 \& k) Q
- if (instance == null)
7 p, j z( g1 M1 b - instance = new SingletonTest(); ; D* ?! V1 ^+ X7 o' S: H/ P$ M: o
- return instance; ) i) j3 V3 O( O! ?1 D3 m' x9 C
- }
( n/ j1 V3 G/ ?5 b - }</font></font>
复制代码 9 d6 X) X% c* @# J: h: s
(3)线程安全,但效率比较低
4 k( P9 w% _) h% [3 H5 u- <font color="#454545"><font face="Verdana,">/**
9 _! _5 ?! F: _ - * 线程安全,但是效率非常低 + X3 O$ q7 T1 L3 }+ U
- * @author vanceinfo / k4 y/ S3 [4 o, Y! K8 y
- * ' `% w: {7 {$ L9 g4 X7 E
- */
- s0 _# a5 c$ y! h5 w - public class SingletonTest { 0 u& l& ]1 ~7 P. I+ Z3 O7 G0 L& P
- private SingletonTest() { ( m9 x7 c; g: j3 ]/ n, h
- } ) Z5 _ A9 u( @% i& j+ ^
-
1 W) {/ I) R6 C* l; `$ c0 z4 A) h# } - private static SingletonTest instance; 8 D! ]: Y" c: a" C6 \
-
% k% V+ Y3 G" z; v/ l5 o0 s' n* s9 k - public static synchronized SingletonTest getInstance() { 7 t$ Z/ z8 _/ h) y- U4 O
- if (instance == null)
3 } s. F+ I5 D4 N3 [* x - instance = new SingletonTest(); ; L1 P* I: P/ X/ x# r
- return instance; 9 F* y8 E9 d; S% ^
- }
5 x7 X- ?% W) F3 E% b. M, w - }</font></font>
复制代码 (4)线程安全,并且效率高5 X3 e* V4 r( h" `$ k4 d8 d* c6 d3 @3 S
- <font color="#454545"><font face="Verdana,">/**
/ @$ K3 D3 B6 ^! q - * 线程安全 并且效率高 % i. Y. M; c/ Q/ }# e/ X2 h7 W" H
- *
, D% B" G( M8 m* W6 P+ G - */
" j0 V6 B/ ?. m& n; G$ C' V - public class SingletonTest { 2 W3 C: W5 i. L; P Q
- private static SingletonTest instance;
4 z; H5 ^' Q* [7 X; G - 2 g U- {: p; r2 v% r- [
- private SingletonTest() { * L, M4 Y4 t; ? p
- } # r% @( g* M( y3 h8 P/ ]# {, C1 z
-
! u# ^/ z+ z7 k! F7 z: K - public static SingletonTest getIstance() { # o4 m) Z( c) q7 V- c6 M
- if (instance == null) {
Q2 n5 s" ] y - synchronized (SingletonTest.class) { 0 ^# A: J) U$ _. |& }2 m- a r
- if (instance == null) {
2 v! t3 [+ l/ ^4 p- K6 d1 a - instance = new SingletonTest(); 6 Q$ b0 v- n4 Q$ s8 u O1 k
- } : l, r$ [, f) l1 {) u
- }
0 q' w& B/ V# Z( y- c1 X - }
+ r/ ^" G3 V p# l/ c& ` - return instance;
0 F. |9 \- i6 l+ U: B' h* Q - }
- x, e$ C: J( }" D6 b - }</font></font>
复制代码 (5)Lazy initialization holder class模式. V* B2 m/ y) F0 S- `9 t6 G
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。, K' h+ {2 ^, q8 \+ U
1:先来看点相应的基础知识
# u% S( [# i! [ 先简单的看看类级内部类相关的知识。 - 什么是类级内部类?
' Q1 n# i' U4 ?% H 简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载
! l J- i+ G! p$ a! ~
再来看看多线程缺省同步锁的知识。; D% G! a# [" Y/ g6 n! j3 n4 s8 H* J
大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时
1 P. y& j0 F E, W6 x
2:接下来看看这种解决方案的思路
+ m9 ^/ s( x" C& h 要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。
7 O a- p+ f. Y' N 如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。
( e) e8 ^/ a4 s) A. [ 看看代码示例可能会更清晰,示例代码如下: - public class Singleton {7 z: x( e8 j8 r! @
- /**
" l5 T1 t( E0 S% o - * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例; e5 B# n; @* F9 q
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
& o7 s" I7 D( ]) Q, A! G7 \# K - */
( r# J5 y9 s' V$ p6 x - private static class SingletonHolder{
$ W! R! L' T) `# \9 F - /**: Q4 B6 g6 a$ j
- * 静态初始化器,由JVM来保证线程安全
+ G& `: ~# a0 @- f1 |' ~( A - */- ~: G( {3 d z; T3 ~1 Q
- private static Singleton instance = new Singleton();! m" Z5 e" G1 P; p
- }% F& ?2 |- E$ |, H' R7 n# [- X" L
- /**
3 k. {) g* F* |8 g4 K - * 私有化构造方法: Q, Z* r3 g+ {6 {0 v0 }
- */
6 P* f0 A1 [1 k8 L+ {, {% c/ W9 Q - private Singleton(){
0 Z% M& Z" j; Z: P' _; s. S1 S - }, L; R4 s$ }) Y& F
- public static Singleton getInstance(){
9 w- f) g2 U R: ^; o! O - return SingletonHolder.instance;+ U$ ]- Z* ]- V+ U* f4 W) L# V/ K
- }4 I- y) s( @: b. {5 z
- }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。9 Y) v3 d8 _0 _& f
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
+ q4 [- Q: l; U* b9 j. ~5 H总结:
6 P9 X4 x) G; e N; O3 C3 b4 h9 {' Z$ ^6 O
单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
3 X; H) @7 {% Q9 B& J ' f0 L: v& l, c0 f* M
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。, Z4 W/ o6 k. | D J; n
. f+ v6 _# P( z$ J2 U) x单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。 . U i" X, M: V1 X% ^ A
! R! b0 i) H& B
0 Z& C0 X7 c( H- ]# J4 c+ K
) [2 G4 b! v; {$ P& Q
) P) I5 i/ k I c: ?% f* o' ]* ? f7 Z5 I2 f, p- M% \2 j
|