Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/**
, U z! }% U4 j Q/ G0 w - * Z- P: x; p- H
- * 单例模式的实现:饿汉式,线程安全 但效率比较低 $ a. O! O* Z: x+ V D
- */ - o# R" b+ u# B! E h* X l W
- public class SingletonTest { + n3 d2 t8 ~' Z1 y" H2 L) i3 [
-
. o' \& D2 L' T5 r* d - private SingletonTest() { 8 J, v, Y# m+ N9 S. N Y3 l5 f* A
- }
2 M" {$ Z) s7 ]3 b* W6 | - 0 a! }4 _9 k/ h3 J$ m6 v9 G
- private static final SingletonTest instance = new SingletonTest(); . A% Q# f" }8 q+ O" V3 {
-
+ I# Q; b: W7 S% d8 _1 | - public static SingletonTest getInstancei() {
2 t; x0 U5 p/ s" q0 E; I: Z1 v - return instance;
$ V. H0 k/ [- C" e6 ^3 d0 E; A) t - } 3 N S6 x3 t5 F: J) m- q" P
-
% P0 @1 x: `& [/ R - } </font></font>
复制代码 (2)非线程安全
7 y. a* w1 I3 {2 I7 i- <font color="#454545"><font face="Verdana,">/** - L- y1 v7 h7 n/ @+ b* Q; ^- [9 l
- * 单例模式的实现:饱汉式,非线程安全
0 z, g! u! P# s, {0 w. j2 x0 i/ ~ - *
. H' A9 ]: T- _& q [ - */ 0 D# C! }! U/ r3 F4 [
- public class SingletonTest {
e1 z6 G9 F( I+ P! z: t - private SingletonTest() {
6 G) t7 g1 N* k3 E: X3 g- x - }
+ @- a6 |1 Y7 g - 5 y* K4 R, d; T7 p: \$ [7 X) H
- private static SingletonTest instance;
& q7 \/ W5 O E5 [4 s5 p$ m* v! g - # a8 \$ z! _' i) K
- public static SingletonTest getInstance() {
! j" n3 f7 ^" \* q- `( m" r( E - if (instance == null) - o4 c3 m; y- [* `
- instance = new SingletonTest(); ! v8 [/ }9 v' L$ t2 f/ ]
- return instance;
& c* \) t% A" j - } + L2 C9 n$ T) D: ^+ Z
- }</font></font>
复制代码
& I2 E5 G# v8 ^6 z- K3 @ D(3)线程安全,但效率比较低
, h4 t1 q4 m1 L, ^$ b- <font color="#454545"><font face="Verdana,">/** ( i: }2 m+ c' v) K2 i" S1 m
- * 线程安全,但是效率非常低
1 g8 V/ x0 T7 C9 e; c( d4 d# \ - * @author vanceinfo 8 _5 h9 A% v* {$ c
- * $ ^2 u& H: d% B- ~9 \& Y
- */
/ k( Z6 e+ T9 T - public class SingletonTest { } h6 |7 w7 B
- private SingletonTest() {
' s8 a4 o9 r- J& b& P4 I - }
! D$ d2 M, H/ @% S+ `1 G& d) C( x1 h - H: e/ ^; o% U- _
- private static SingletonTest instance; 7 _! ~) x! A! r$ W; E+ L
-
3 j) P8 h6 i2 O2 D' ^- r* E, s - public static synchronized SingletonTest getInstance() { ) n+ C$ K/ E' {- d% q, G! x' c
- if (instance == null) & e) U. w9 w; H* _& \( Y8 T
- instance = new SingletonTest();
0 z7 S* i( P8 r# z ~ - return instance; 7 J, @" M$ K6 C, v: F1 m2 `
- } ( L1 ?0 \8 n1 c! A0 |3 V# t
- }</font></font>
复制代码 (4)线程安全,并且效率高% M J5 m2 h$ G2 O; e* ^
- <font color="#454545"><font face="Verdana,">/** / u1 [1 @& Z( J* b
- * 线程安全 并且效率高 6 v4 F4 i7 d7 n; t3 s$ V x
- *
5 N( [% s" U1 |8 K - */
0 v' {, m# a2 h' r) l - public class SingletonTest {
& l$ f! z: H- f) P, }0 k. } - private static SingletonTest instance;
3 r) }9 i. ~, @$ {- f+ F0 ]% G% Q* H - & v1 _1 y- D8 v* v* q/ P
- private SingletonTest() {
6 D0 B0 d- ]% x) r3 x0 u - } ! y+ M' d4 _7 C+ E/ p' X* X3 t" V
- 1 `. t, g1 ?! B5 l3 c S0 v. f
- public static SingletonTest getIstance() {
9 j0 |- {; j/ ]7 I; ~7 R - if (instance == null) { ; O( d! ~2 o9 {2 z$ u7 B7 Q- \& m8 [
- synchronized (SingletonTest.class) {
& k% s+ Q' O! h& z, i - if (instance == null) { 2 h4 }! O: e) b7 `) h6 |/ J& Y9 H
- instance = new SingletonTest(); , R, \& _" K! p9 s# W2 ]) R
- }
( ]: I, k/ p# S/ {( s3 c& n - } 9 _% A3 P3 S6 H1 {. ?9 B' o
- }
- j' H. Q9 b3 o7 @' b - return instance;
$ G5 b$ J F e2 w - }
& G# }, {7 K# ?! P# D* w, p - }</font></font>
复制代码 (5)Lazy initialization holder class模式* q% \8 ?( g( m) [( E: p: t' M! E
Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
, p" w4 W+ B( `1:先来看点相应的基础知识
* W" R9 Y1 g+ ^; Y3 J- z 先简单的看看类级内部类相关的知识。 - 什么是类级内部类?
- d" O) T2 k! X5 W3 H 简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载
" M. p9 r" x( L3 j7 g& ~
再来看看多线程缺省同步锁的知识。
. m# C# Z/ i" s, s8 W 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时+ D* z4 V2 B$ u {1 O) }/ [ o
2:接下来看看这种解决方案的思路1 C* L$ ?! b% y- A
要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。! j0 U: ?( a1 Y' w7 C
如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。
3 Z0 v: N: T# m) B; ?8 Z" a 看看代码示例可能会更清晰,示例代码如下: - public class Singleton {
0 J# c! c$ ?) [ - /**3 V+ `) M0 x3 Z, X: I5 D
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
8 K3 t% _/ S8 K& ?+ ^ ^( S. a - * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
1 k' R4 A2 d1 s - */
( n' ?. k( [8 i) s5 I - private static class SingletonHolder{1 s5 _* g) b" t+ r
- /**( R+ _/ M B8 t3 g3 w- e2 @
- * 静态初始化器,由JVM来保证线程安全
7 d2 Y7 `4 O7 O7 F9 m. t2 e, w/ [ - */
& Q. H9 |7 [1 |" F# V7 [8 B+ J - private static Singleton instance = new Singleton(); n- m6 A; \2 Y
- }' s3 {, ?; ~. f% C
- /**
( d4 ]) Y: E2 C - * 私有化构造方法
+ ]& {. Y4 t2 g' Q - */4 M" U/ R( Y& ?0 s7 M& x
- private Singleton(){! m) Y# c8 v2 s7 H! N& y9 e
- }
$ \. A# T5 w4 z+ e+ f/ r3 v - public static Singleton getInstance(){
; _4 f: D2 ]" B( M# X' u0 Y - return SingletonHolder.instance;( y+ K4 H: |$ y5 t
- }
7 Q1 K" e8 H7 ?" t+ p2 T5 S - }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。! }0 B7 [- u/ y1 M) H
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
2 f: }- b5 k: p, P" ^总结:
% {+ B) e% L: K' g
3 q7 ^5 z D# G+ W9 p/ w单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
5 w3 F6 J! m. q7 \6 \
! ?2 P: U2 K0 M% \1 ]; Z$ ?; d单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。; o: e4 o1 m& u) C
( ~9 J) Z. V* @; ]$ b, J! p单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。 5 P9 ^0 ^* G- `7 F3 X3 Z
( I) H8 |& _# i* g9 W# v3 Q
1 t, b% `' t8 R! |$ @5 B; s& t' K7 \
/ \7 [( i& V8 L& j
+ m# L+ `. S* A& h: Y+ B& H2 `7 J) G: L f% N# |' B
|