Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点 实现的方式有如下四种: (1)线程安全,但效率比较低 - <font color="#454545"><font face="Verdana,">/** " s/ w& f7 |0 }, b
- *
3 d3 r, i: k* N - * 单例模式的实现:饿汉式,线程安全 但效率比较低
) h1 M4 g: Q" Y) W2 R, { - */ g5 x: Q4 ]+ _, Q1 x) Z
- public class SingletonTest { 6 i" o+ {9 @9 c0 ?
- $ ^6 v, O. J" E0 i5 ]. Q
- private SingletonTest() {
$ z0 D( ]4 m3 ? - } ) s+ D1 G3 k8 O. x- j
-
; q# d3 N! f6 j" [8 C - private static final SingletonTest instance = new SingletonTest();
) m: B. E) Z* _ -
8 v$ U2 V9 j. I4 | - public static SingletonTest getInstancei() {
' j9 ~ G6 s, K8 J5 e0 Z# ]! Z - return instance;
' D' m4 j B* q/ q - } : I2 \& K5 k' G1 ~3 ^5 u
- 9 f% B6 W- v. D* C4 R1 a
- } </font></font>
复制代码 (2)非线程安全: q$ |, ~% ?9 y$ F6 X+ [: ^ T1 ~2 }
- <font color="#454545"><font face="Verdana,">/** 4 s3 {. N- t. Y1 l+ a# b
- * 单例模式的实现:饱汉式,非线程安全
4 s" K( R6 a! W8 q7 R - * ! s" t( X0 U8 b/ N! X
- */
2 ^# Y% Q7 y; N, D5 `) e) {0 N1 O x5 F q - public class SingletonTest { / Y, N/ x+ r* T- b* [# E
- private SingletonTest() { 6 C) U$ m6 M( {/ A+ a
- } . {3 z4 E! K( g* P/ ?8 T g% N- k
-
+ e; O7 u" M* a& e' ^7 U" z. Z - private static SingletonTest instance; & v2 i7 C, p" f
- ) W+ z: v' N4 A# Q/ d' x, F3 o
- public static SingletonTest getInstance() { : g, x% G- \( n9 g
- if (instance == null) : T5 _0 B: s* O
- instance = new SingletonTest();
! U2 | ~7 u1 _* H - return instance;
, ]4 }1 L2 b ~" g' n, k W1 e - } : g4 B+ c4 Y. {$ `
- }</font></font>
复制代码 : p3 }1 [+ w" `7 d
(3)线程安全,但效率比较低 |' Z7 l& p- c" b
- <font color="#454545"><font face="Verdana,">/**
" I% P" |8 j: j; m5 ?9 O - * 线程安全,但是效率非常低
& B6 P8 d# ]$ _+ K2 j- f - * @author vanceinfo 2 X/ E% M( H1 K: h
- * # b" o3 F; p* P8 n U: N) D" P( G
- */ ) C* K+ k" B) g2 L
- public class SingletonTest { ' ? e' t7 d! }" _& V! J
- private SingletonTest() {
( t- d; [; m3 ?, H - }
3 t8 a5 z# H$ V; ?% _+ b0 E" x" z - 1 S& v; q! C9 W0 F( S! d
- private static SingletonTest instance; 9 v' C6 \: `" M1 }
- + U1 ]/ O9 b3 F$ x* ^
- public static synchronized SingletonTest getInstance() {
b# l8 H4 r% ]. { F, V, W" u5 T - if (instance == null) 8 g7 ~) A" I$ g P& E! O' P0 @# E
- instance = new SingletonTest(); , ~1 G6 x. O3 s; P9 z
- return instance; 5 H+ a+ Z! x8 n! P- `# c/ @
- } # l9 v+ {: r% }
- }</font></font>
复制代码 (4)线程安全,并且效率高" X3 l9 r3 z$ b; V
- <font color="#454545"><font face="Verdana,">/**
" O. `) P5 U( ?0 a: D& u - * 线程安全 并且效率高
5 O% A; @- L) x/ F2 S- g0 d - *
$ v [& h1 O" X8 \9 A6 n - */ 2 `/ [# T! U, F9 Y% E
- public class SingletonTest { % W7 g" }+ L+ v
- private static SingletonTest instance;
( K' U) g5 g f& M' a( H- i5 m -
6 B' h; ~& _5 Q+ ^( z - private SingletonTest() { 2 F0 u1 ?. l4 Z% j
- }
, B: p" V" M! ^- z c* Z5 T } -
1 t0 `: c2 h- w* s3 V& C - public static SingletonTest getIstance() { ) i T7 ? p! S# g- E3 r
- if (instance == null) { 1 z6 U- t* [9 |7 }
- synchronized (SingletonTest.class) { / `. K/ k% X# C/ ]
- if (instance == null) { ( f' r; I* k% u% b" ~5 ~
- instance = new SingletonTest(); : C* C) f+ Z$ x. Q
- } 5 M- H' p2 E0 n: G& F8 \
- }
/ C' _' {6 K `" n* m1 D - }
% I- J) T0 i" o# E - return instance; ( e5 T' Q7 v' O" Z" }" h9 l
- }
( L) s1 g) U" W% L- X& F! } - }</font></font>
复制代码 (5)Lazy initialization holder class模式
1 j/ @. \; X; y3 b; w; ~Lazy initialization holder class模式,这个模式综合使用了java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
8 S4 S2 Q4 ]3 p9 c( G1 C1:先来看点相应的基础知识/ r3 i# B2 m/ J j& ?8 g
先简单的看看类级内部类相关的知识。 - 什么是类级内部类?
1 d$ C: Z2 [5 \( b 简单点说,类级内部类指的是:有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。 - 类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
- 类级内部类中,可以定义静态的方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
- 类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载
( d1 X; @+ H$ n5 N$ z
再来看看多线程缺省同步锁的知识。
4 y5 U5 M( w( r1 q% P 大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括: - 由静态初始化器(在静态字段上或 static{} 块中的初始化器)初始化数据时
- 访问 final 字段时
- 在创建线程之前创建对象时
- 线程可以看见它将要处理的对象时/ Z5 K9 C* j, M; [9 X6 W" E
2:接下来看看这种解决方案的思路
2 L( }- a! V$ V0 n6 c 要想很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程安全性。比如前面的“饿汉式”实现方式,但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。
" q7 G2 N% {' @' T7 l9 W 如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例,这样一来,只要不使用到这个类级内部类,那就不会创建对象实例。从而同时实现延迟加载和线程安全。
7 p2 E. z" M7 M1 M' X8 [ 看看代码示例可能会更清晰,示例代码如下: - public class Singleton {5 _- C! i# r9 f' A
- /**" s. i, W' S& N6 U3 ^- P
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例! l" a) j: |6 ^2 D* t5 a$ c
- * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载9 Z- J1 i1 K: V2 U& `. A7 t
- */+ L* o# Q. `! i7 C. [3 |
- private static class SingletonHolder{
4 D( O- M# W3 h" n- ~" u0 D4 ^- y - /**
( Y# R- A. y$ \3 {( H - * 静态初始化器,由JVM来保证线程安全
; G* M$ ], |% p; l; S5 T - */
. O1 d/ y# @( ~- H) P& [) a - private static Singleton instance = new Singleton();
0 J; q$ x' Z, {% o - }( m5 T" a: Z$ {$ A$ W0 v1 M
- /**
: U' Z: o2 i: t& y2 p4 B( x - * 私有化构造方法5 W: D; q+ z4 F& f8 w9 t
- */
1 c' x* }+ R) i9 v { - private Singleton(){( E- `, u+ G9 |
- }
1 l7 J8 x. ]. O/ H - public static Singleton getInstance(){
6 ^, M1 }6 J0 l* a( Z { - return SingletonHolder.instance;! a# ]' q2 B q# ~, q7 r* y0 N
- }
. e: ^9 V7 Y( _4 I4 x4 R6 }4 `2 v - }
复制代码 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。+ J1 u7 p( `1 h4 E
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
0 N, k' Q _8 ~总结:
! D- N0 l( A8 G/ I6 k
, f7 e# q' u3 X9 g; X9 j单例模式的优点: 单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。 - 实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
- 伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
& O! \, i$ b* Z$ j4 s
! K$ C, x7 ~# }2 v单例模式的缺点: - 系统开销。虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。
- 开发混淆。当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。
- 对象生命周期。单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。4 j4 N: r# v o! R( s9 [& C
$ S+ x }# d+ [3 u: U/ ~; P: _# K单例适用性 使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。 不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。 不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。 . t4 Z6 W, z- U' S
1 k% n0 T3 Y4 T. [$ Y. U
$ z6 A/ ]2 o, ~2 H$ q8 m$ P2 \) s6 @7 Y- Z b1 ~8 i
% H# M$ g `; V% x) ^1 z# n
4 C% S1 D1 q1 T0 O6 z f* C+ B
|