Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
( f1 H0 b) Q& l) |2 U- a - *
. G* i; K/ N# G - * 单例模式的实现:饿汉式,线程安全 但效率比较低 2 ~7 n, k/ F4 _0 n( R; ?. O# x7 |4 ~
- */ 1 `0 k4 \" t7 W
- public class SingletonTest { * R: t8 Q6 Y7 `+ [7 |
-
' n" [/ |! N. A3 I) G3 s3 N - private SingletonTest() {
- M- x" f- Q+ ~$ Q6 f/ H9 f* i - } g4 w' M0 a- P' Z p, i* @2 s* t
-
- ?& t, `4 w/ L" ^+ w$ b - private static final SingletonTest instance = new SingletonTest();
. w9 w( Z. y7 L9 J; t! Y4 ] - ( @ b' `) e W8 ^" z5 W J% C
- public static SingletonTest getInstancei() {
6 X B/ J$ r; n) u, X - return instance; ' G }/ L& u) V! p% S& \! h
- }
3 n. m8 r& o, k% Z Z' F -
* b/ P& j4 b; F7 _- t - } </font></font>
复制代码 (2)非线程安全1 k1 j1 e$ d* I a9 a: ^
- <font color="#454545"><font face="Verdana,">/**
, q1 o" Q( V# w' }* p0 r - * 单例模式的实现:饱汉式,非线程安全 5 Q% w2 s" W& D6 h P( p- X
- * " ^. y0 J8 I# f' R% b( @
- */ " U% _2 m6 K/ F& K$ B1 g: w
- public class SingletonTest { - g! j2 `5 K' ^) P1 f
- private SingletonTest() {
( _0 b9 m8 k9 C, | - } & t) @4 a" T0 S$ v- W. L
- , I1 o7 X2 `4 y7 U" w, G
- private static SingletonTest instance;
0 w. D$ M% W0 @. C; M) L. W" V3 y -
/ ^$ B/ f; j. C& n: Y3 o% n - public static SingletonTest getInstance() { 5 d1 I" z* R* `, O4 F3 l* l; a
- if (instance == null) # B" ^0 j& e# A2 ^' P: V1 f" I
- instance = new SingletonTest();
& K) s/ y2 G! K7 W! I8 u0 j - return instance;
7 [1 o" L) q9 ?* {# |3 O. V$ g - } . F$ K1 I2 G3 W9 H9 I; S
- }</font></font>
复制代码 - p6 y" p3 g! J: L
(3)线程安全,但效率比较低
9 L* ]7 b# A" {9 z! j" q- <font color="#454545"><font face="Verdana,">/** $ C; K# I& b: l# K. q
- * 线程安全,但是效率非常低 9 [/ U; Z# Q: P t+ V9 N+ s
- * @author vanceinfo - f, F4 H+ Y. A! X5 x0 W* l( I
- * * @2 |" `/ Y! K$ e
- */ * R% {+ {: _6 h$ H7 n0 ^' S3 `
- public class SingletonTest { . F3 K+ u( K+ G i$ ?/ A
- private SingletonTest() { & Q' z4 G% ], T/ j1 L
- } % H7 F3 I' X: G! K, S
- 5 X2 |* T. V& ?/ M# [' X* ~
- private static SingletonTest instance; ! e. i5 [' n2 x4 D' V* i8 j
-
' V! c$ e, J. G4 Z, Y - public static synchronized SingletonTest getInstance() {
0 q# ]1 N& V1 t8 @5 Y - if (instance == null)
; O' x7 j. h5 \- d; u7 { - instance = new SingletonTest(); : v- Q; R* U# Z! _ H
- return instance; Z+ M6 X' P4 o2 l% O' M/ H
- }
$ j( ]8 \6 _, F$ r( e: L! L9 i" Y - }</font></font>
复制代码 (4)线程安全,并且效率高
5 F s1 ?% X+ j4 N. w" A- <font color="#454545"><font face="Verdana,">/**
# @0 E, U2 w9 a2 O - * 线程安全 并且效率高
6 ^. @! D n# M% J& r' ^ - * : ?3 r9 S8 ^4 g6 S2 F5 t- w
- */ ( i, s8 Q. B7 G0 [! X" |8 B
- public class SingletonTest {
$ P1 A6 `$ T3 ~ - private static SingletonTest instance;
) m! |! y+ C( K5 _( s* f -
9 N! v6 B, N4 j# Q) n; C* T) d - private SingletonTest() { * M/ d/ [/ i0 t
- } ' I U" m! i h; p% e/ t0 {; g6 i
-
3 X8 l+ @9 |# a7 ^! H - public static SingletonTest getIstance() {
7 g0 _' v1 s7 v4 x3 [2 O - if (instance == null) { % d7 b+ G7 I* F
- synchronized (SingletonTest.class) { . A" g8 K8 E9 s g( |
- if (instance == null) { 6 k, T Y. a. ^" ~
- instance = new SingletonTest(); " W9 C: h' q1 C
- }
! [- W- \+ G" N/ t1 } - }
( }* ?" _: V* f( `6 T% E6 }5 w - }
* N% x$ s% i! d& Q - return instance; # q. a6 k& ~ i% V% O
- } 8 M6 P0 g0 j& f
- }</font></font>
复制代码 (5)Lazy initialization holder class模式3 l4 M0 ]$ S6 G0 o% L7 w U
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
3 n' K9 |( G. J: k4 n5 ]1:先来看点相应的基础知识6 o, r. @! H9 v% h. F
先简单的看看类级内部类相关的知识。 - 什么是类级内部类?
# F% `2 k; x; H2 W' O) p 简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载% {8 B4 V! b" ~- g+ Q4 F
再来看看多线程缺省同步锁的知识。
; q2 G& M5 B& A# j* _# _3 k; h; J 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时
$ J8 c4 t- k5 Z# L# v7 r+ r
2:接下来看看这种解决方案的思路7 r: m* g3 w' p* a% q8 U
要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。! ~9 ]' g& s+ F( n# k. K3 X# s) k
如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。) v: ~* {- z" i9 L( \: E
看看代码示例可能会更清晰,示例代码如下: - public class Singleton {
# Q- P+ v1 `( ?7 E/ F - /**
# B1 g3 `! S4 _/ ^/ _/ N3 P - * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例; |; @' o& U! b
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
. t- p9 h/ D) I - */0 x6 m% A& V. n- D7 L2 G4 J9 s- j
- private static class SingletonHolder{
% I# p' a3 t8 l4 T - /**, L. y/ @; Z! ?' c" B9 C
- * 静态初始化器,由JVM来保证线程安全
3 U" p/ [; n7 z9 H$ | - */
) Z; H- t" j' E - private static Singleton instance = new Singleton();
3 Q: ~1 c4 O7 S7 |2 L6 X" S - }' {/ _6 J' D$ _: g; X" K# {
- /**- s, n" F* @, X) l6 H) ~
- * 私有化构造方法9 a+ m, f) M: x O3 J7 u) U/ T7 R
- */% r* E9 u3 g1 J8 d5 U# a4 Q% M3 @
- private Singleton(){3 r( c& i$ m# } S
- }3 I' F: i- h- r/ g
- public static Singleton getInstance(){- U* Y. k3 ?- P9 d& S) ]
- return SingletonHolder.instance;; ?* K- M9 Z* R9 S
- }+ v; \# t6 z3 x# O5 Q0 L
- }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。# D. k7 b8 |( {1 X
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。9 G* F1 z" s/ m( w! l- Z
总结:( E$ g ?1 [; E" D6 k8 k
; D0 U. U7 p2 u
单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。$ |; A) Q+ r/ B' h
( H" O: F/ }. e8 s0 i0 }7 V
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。5 h/ u7 @5 M4 v9 |
$ e* j0 V/ w* a" V7 o单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。
3 O- u4 \8 g- }4 j ~* b- Y. i/ [
& b g, z$ O: i7 P; [5 ~6 y( N3 _/ g2 ?# j3 C7 j
2 D6 }6 q) |! j
5 Q6 k- A$ [: q. w0 P7 p' O; h* T* u& H
|