Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
9 {! A4 F c$ {& e1 R2 i% t - *
: L8 M& F3 x6 y) C- M+ a+ B& | - * 单例模式的实现:饿汉式,线程安全 但效率比较低 " @# `# e P6 D6 z. J R
- */ " L" T$ s+ P7 R
- public class SingletonTest {
% e: _8 `! v( t( Q, y - $ a& e6 P7 B6 W
- private SingletonTest() { + k) n6 A Z: J5 Z4 z0 t- [
- } # m* }2 _: c [' ^- W
- 3 [) G! `3 H4 g
- private static final SingletonTest instance = new SingletonTest();
, U4 z- C% Z4 m! {- O' I -
, z/ f; y2 W4 }, j6 L - public static SingletonTest getInstancei() { 8 ]* {2 N; ?; k
- return instance; 3 O8 m1 f/ g0 S
- } 7 G1 G$ X/ J s. {. ^, v
- % L/ e4 k+ K2 y
- } </font></font>
复制代码 (2)非线程安全
' ~) n ]) A: a' _- <font color="#454545"><font face="Verdana,">/**
5 `5 j6 D) a5 z; a, k - * 单例模式的实现:饱汉式,非线程安全 9 M8 G2 ~# w$ ^' d# x
- *
% i; B* z. P# D - */ " b* O/ R: q! O, t- b$ ]
- public class SingletonTest {
8 P- B) h# x' _4 G, t - private SingletonTest() {
5 b# w3 M; O4 \1 r" q - } + k" L5 n2 B$ z/ Q/ E/ l3 O
-
5 E6 C* T8 a/ M9 u1 z - private static SingletonTest instance; 4 I) P" e. y/ f8 ~4 ^4 f
- , G, l' ^1 v5 H! `- N+ [+ h% @; x
- public static SingletonTest getInstance() { ! R+ E" V2 i a, C) S" V0 m9 Q" x
- if (instance == null) ) F) l6 K: \2 \5 ]1 I3 B0 g7 C
- instance = new SingletonTest();
; Z# s+ g$ Y6 }3 ]" Y, D% A - return instance; Q4 M+ g/ T* {! j2 t
- } % f3 Y. `7 s' B7 U# o/ T* U0 I
- }</font></font>
复制代码
' S# Q$ a& b% I8 P+ H(3)线程安全,但效率比较低- [/ Q# [/ }: ]' z& J* r& j b: N
- <font color="#454545"><font face="Verdana,">/**
& \. E7 K4 f' d2 O; Y5 C - * 线程安全,但是效率非常低 : s, A3 v' [& t4 U$ m9 b
- * @author vanceinfo / G- K; \% _4 }
- * / o& O, i+ \& w& P& c
- */ $ Z% H$ W! ~7 L1 L& Q% f$ w
- public class SingletonTest { ( D% J$ x9 I: F* R, _
- private SingletonTest() { % K- O( a. e3 G$ ]; k
- }
: |7 K' R' |! `* P, w - - o( K( Y& Q1 i7 ]* p+ O
- private static SingletonTest instance; / |6 ]9 q4 h5 o$ s5 o2 ~% y
-
7 U2 b b# p4 O) Y - public static synchronized SingletonTest getInstance() {
. w) ?1 t& e- c9 |5 Q! l - if (instance == null) 2 R) l9 b7 H9 o c9 l
- instance = new SingletonTest(); , J0 z6 H- N& s; x$ g! Z
- return instance;
/ m1 S2 f* C" N/ t N. Z0 v7 d - }
# q; A7 ?) d! E0 v. P7 a5 Q- q - }</font></font>
复制代码 (4)线程安全,并且效率高
/ M' A1 E/ @$ c* V5 T+ J' v* H- <font color="#454545"><font face="Verdana,">/**
+ a3 [1 j( `" B) C5 U - * 线程安全 并且效率高 . _* |9 q& Q. }0 A$ @
- * , h, @1 d' N: I- o; G
- */ / K! k* k, W2 u! n
- public class SingletonTest {
( W/ |4 z: }" O - private static SingletonTest instance; - k$ h }7 v2 H; E1 f7 H, E1 U
- " K3 U9 U5 ]: T4 I( q
- private SingletonTest() {
5 a. T1 J' ^+ Y3 w0 l - }
7 }; F7 e+ u# F) i6 _! d9 \ - 1 M1 r8 h" [- i$ |- U0 r
- public static SingletonTest getIstance() {
% o8 G9 u5 b8 [) N+ ~3 \ - if (instance == null) {
; J3 @- L% \, [: o0 b( T - synchronized (SingletonTest.class) { ' F1 C' D4 i% x
- if (instance == null) {
- W( b9 V! l5 F& \ - instance = new SingletonTest();
3 k9 r- |$ O7 K4 |2 [ - } : P. A+ @5 k+ N7 S( ^
- }
- t- I* J) s. D9 o+ I$ w- l6 f - }
% }' A# ` |7 x+ b9 _! } r% m - return instance;
2 o* M5 S S0 I: S3 ? - } ' V0 Z* _9 X i; b. x
- }</font></font>
复制代码 (5)Lazy initialization holder class模式' T8 ~: {! m5 J9 t
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。; m, s8 A1 L7 z
1:先来看点相应的基础知识 G2 Z5 \* a ^, c
先简单的看看类级内部类相关的知识。 - 什么是类级内部类?: Z3 [# j) f" H- W$ Q
简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载
+ M. L' f0 W- O- q
再来看看多线程缺省同步锁的知识。
% s- B1 Z* I5 c& ~( v d 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时
+ q0 c( B7 s; _9 h4 s
2:接下来看看这种解决方案的思路
8 }" W: |0 Y& I% G/ @ 要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。
7 \+ |3 P* z1 Z# v0 h# E5 L 如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。* ?* B o/ R5 j U8 q+ l
看看代码示例可能会更清晰,示例代码如下: - public class Singleton {2 c/ ]7 j' T/ Y3 c7 a* |/ E3 @
- /**/ h ~ u5 C, h; V
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例* @: V. E$ l. N* B7 T, r; @9 C* w; h
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载/ M' k C% [* M
- */
7 }/ @$ X3 d, c: m - private static class SingletonHolder{; g1 R' \) v' u9 _$ y6 a
- /**
* d% c' u* l G) u7 S - * 静态初始化器,由JVM来保证线程安全
$ _* E8 @2 b: s ]) [( e - */
" x( _$ z! D% e - private static Singleton instance = new Singleton();
L! L8 v" o" N4 d, Z X - }
^+ r8 U3 `2 G' D' A1 }( q2 h - /**7 |3 h# l6 |$ Q0 X
- * 私有化构造方法) `8 o7 t; G. q! k& K2 v& L
- */
: q, N0 h8 B" t( @' D9 |4 K& K - private Singleton(){
{ u/ A {+ R( x - }
+ v8 X% `9 ?4 K1 H0 F - public static Singleton getInstance(){% L: Y5 x/ l7 H( v- d
- return SingletonHolder.instance;
# m- p; D1 S$ \( v$ h( f - }
$ |' L$ b/ ?$ M - }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。# I& d. m# b5 t" p
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。6 n7 d- z7 q% ]! Q9 Y
总结:
& e+ N+ J: q. D( V. ^& q: J, t: ?% [7 J9 r. A ~/ w
单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
q/ h3 l: G& A% n d2 [/ @! t" [ . ^/ o) d! h% n! M9 `1 F6 J( D$ T
单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。" c9 [8 D" z" T) {1 r0 j
s/ @& E( k8 d; K单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。
8 r t1 x8 @) M" h4 V5 I' A. k
! K3 u7 d2 w) C+ W- j
$ _9 W. r% e1 h" n! G3 u( Z: K+ }$ N: b8 d) n( i2 o" }
. g1 j3 U% I3 w# a' f; b) D) ?( N, x# G
|