Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
0 e5 V$ [0 X5 S; } \ - *
( O% u1 |: Q( N5 B* S - * 单例模式的实现:饿汉式,线程安全 但效率比较低
" ~. Q6 U0 i1 ? - */
1 N8 N6 m4 q" m% |" Z" \& F+ J5 ~ - public class SingletonTest {
7 i, b0 d: m! T - " Y9 W% `4 x, o
- private SingletonTest() {
# A% p" V, I- l# ` - }
# z0 d4 T1 O7 f% d! g" v - # ?+ g S3 t" M1 d+ }9 R" t
- private static final SingletonTest instance = new SingletonTest(); 5 A5 A0 l3 i3 D; I- l5 T/ ]
- 5 y( o+ n2 Z( W7 {8 l G8 v. G
- public static SingletonTest getInstancei() {
5 J0 ?. j6 F' [2 ~3 [0 N, i - return instance;
5 r2 `/ d+ ` u! l, R& Y# c) S - }
& S1 y* e! G8 V4 f9 H6 e3 m5 N -
" M4 d6 x/ d' j& N; I) X; P+ b - } </font></font>
复制代码 (2)非线程安全& t5 C* `' |! ?+ f7 S4 J- T
- <font color="#454545"><font face="Verdana,">/** , d/ F; Q& Y* P+ s% n
- * 单例模式的实现:饱汉式,非线程安全 1 {7 H0 U* X6 G3 d# n0 c9 y
- * " {; p! ?8 z' z* O9 y/ j6 X
- */ # |$ Y( ^* |. o! T
- public class SingletonTest {
8 q# X* g4 Y/ I( E/ d a( Y - private SingletonTest() { $ W4 ^) Y* r+ m; S/ O9 x
- } - J A7 y+ g/ N7 }* P* t! t
-
, P& ~) M' g( E- V. ?' e9 L - private static SingletonTest instance;
$ K u. d1 z1 m. E- f) n# ? - 5 m" Z o/ X' b3 {
- public static SingletonTest getInstance() {
" g ~# G- t3 |5 h - if (instance == null)
% I: S* n0 u" F, V' Y4 ]5 w - instance = new SingletonTest();
4 Z- u* ~( U- X. `$ a* P - return instance; " m9 J. H* C9 B V# c3 S1 Z
- }
! b3 Z! f& G" ?% C, y: _ - }</font></font>
复制代码 3 E) s" C8 @8 _
(3)线程安全,但效率比较低
! `1 f) O% {* }& ~* [- <font color="#454545"><font face="Verdana,">/** 1 B3 E+ ]4 O% R/ b' J9 f0 Z
- * 线程安全,但是效率非常低
, M. \4 @2 F0 G - * @author vanceinfo
) ~6 s! d3 j9 S - *
) x1 I* ^) g- \ ]0 c# _ - */
3 C' W. R. z2 A! l8 d2 ? - public class SingletonTest {
3 ~7 F7 d3 m, @8 \ `6 u2 j - private SingletonTest() {
7 I5 l1 u H" ]2 ^ - }
1 M+ m: I+ S3 M -
: \( q+ W$ j4 e" k- E% j, E7 l' [; Y9 E - private static SingletonTest instance;
; J. T7 S" j& V; u# r; ] - 5 @: P! a! C6 Q: ]# b
- public static synchronized SingletonTest getInstance() {
/ j# ^3 W _/ Q0 J - if (instance == null) ! S5 |' t. p- R' l0 r) q( |
- instance = new SingletonTest(); ( m5 N' c P; x! @! W' E
- return instance; 4 F2 T3 }0 ~! O+ I
- }
2 y: {) @: t. v- X" U+ a* ` - }</font></font>
复制代码 (4)线程安全,并且效率高. t9 S7 @, Q' X( V5 n: I
- <font color="#454545"><font face="Verdana,">/** # n! }' B1 o0 H E' p( C3 G7 {. n
- * 线程安全 并且效率高 * E; t; m$ [9 D. p9 q1 K, w: f2 C
- * 8 ], m9 q: K$ U' I- z7 u
- */ y1 i' ?$ G+ W5 k" j
- public class SingletonTest { " u& G. t; u. z$ ~: _5 w
- private static SingletonTest instance;
4 v9 V3 M2 [. _/ |+ x. Y- w -
( s2 N8 F- @' K5 K7 b/ I1 D/ j - private SingletonTest() { % F/ ]5 r- T, t9 f1 `! T7 y) a
- }
( |; Z" ^" x0 p. `9 e- U, e7 J -
- K; _/ V7 ~. A/ [! l. [8 G - public static SingletonTest getIstance() { $ U Q' z9 e0 g7 u, C% ~1 t
- if (instance == null) {
0 ~/ j t9 }* l( x - synchronized (SingletonTest.class) { 0 Y4 ^8 ~; P: f6 l l
- if (instance == null) {
O: K# b6 m& h) y% E+ l - instance = new SingletonTest(); * X. p3 O) } V) s4 y
- }
4 f, r7 r7 l/ n6 [5 U+ z0 ~4 Z - }
4 f' c9 x/ z* l0 x1 [ - } ' V( n8 o$ Q3 t9 H
- return instance;
+ p4 q) s/ |+ [4 A' r - } , N( t5 p: t! c
- }</font></font>
复制代码 (5)Lazy initialization holder class模式& w& H1 S4 D; a8 @/ g, m
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。5 b6 A: O& N0 c O
1:先来看点相应的基础知识+ t* K5 `% e. M! ^/ Q$ X* o
先简单的看看类级内部类相关的知识。 - 什么是类级内部类?" E9 D# X6 ]* h( K; \
简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载( M( Y! {4 U0 X, H9 }: X, }! q+ h
再来看看多线程缺省同步锁的知识。
T: d( n+ m" L7 K4 n7 v0 `# O 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时 m, e% C! u. J7 H1 j4 q3 v/ C2 T
2:接下来看看这种解决方案的思路
$ j, \) |! y8 S; g+ N! D 要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。3 ? V+ K- e3 ?7 L
如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。
/ G" g" B/ ]8 x" {; V, \ 看看代码示例可能会更清晰,示例代码如下: - public class Singleton {' S, n0 n# p! F# _. b! w3 {9 c( h
- /**$ F, R* n2 k9 j J* S2 n0 e; f
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例, z. U/ D+ a* g! n {
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载) a) d# r+ m w6 |* E
- */
- V& h! l8 j n A5 v& p - private static class SingletonHolder{% l6 D' ~% T0 J! D$ A# f# d0 |$ h% w
- /**
, E; R4 x; L2 M' M4 e5 o! r - * 静态初始化器,由JVM来保证线程安全
0 G0 y5 o; X7 u- f9 \ - */% R, J+ G) k3 H# t6 b8 A
- private static Singleton instance = new Singleton();' I& u0 Z- p0 |& O. a
- }
4 ~# y# L: j! u8 q. F - /**
! a `% b# U9 z J - * 私有化构造方法. d- L* N7 U1 m8 B, l% X
- */
; @ i! }' f) d% H - private Singleton(){
% }) n& x1 P; E8 N1 Q! X/ R - }
; E# |/ h4 f% N7 o7 J - public static Singleton getInstance(){
- i4 u3 ?- {& ^& N - return SingletonHolder.instance;2 I1 d% ]& x% b3 |
- }
/ T4 Z+ R! S+ g& s6 }" n' P9 ? - }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。% C- J' I' I6 X* ^$ b- a3 z1 p
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
6 q' S1 A" x3 U) P总结:% {! b6 z- R c, L" h1 V3 k9 l
! Y1 F! L$ z3 g9 X6 ^
单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
2 |4 y s8 f9 H+ h * v) w7 @- l- ^8 |
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。
' D9 _. m7 k+ p" [. |* k 4 ?9 w W5 h: r5 G; P/ g( W4 h
单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。 % w7 o1 x* X; R; k
+ o* A2 a9 e* w0 \) f# a" m4 d
) j* h% H! O3 W
& I6 x9 R3 t; D$ ]+ C& Y: J H0 ?3 P( C( B \2 w
4 N3 z5 y2 h& Z. N |