我的日常

登录/注册
您现在的位置:论坛 盖世程序员(我猜到了开头 却没有猜到结局) 盖世程序员 > java单例模式(Singleton)的5种实现
总共48086条微博

动态微博

查看: 1434|回复: 0

java单例模式(Singleton)的5种实现

[复制链接]
admin    

1244

主题

544

听众

1万

金钱

管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    跳转到指定楼层
    楼主
    发表于 2014-11-14 11:04:15 |只看该作者 |倒序浏览

    Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点

    实现的方式有如下四种:

    (1)线程安全,但效率比较低

    1. <font color="#454545"><font face="Verdana,">/**  " s/ w& f7 |0 }, b
    2. *   
      3 d3 r, i: k* N
    3. * 单例模式的实现:饿汉式,线程安全 但效率比较低  
      ) h1 M4 g: Q" Y) W2 R, {
    4. */    g5 x: Q4 ]+ _, Q1 x) Z
    5. public class SingletonTest {   6 i" o+ {9 @9 c0 ?
    6.   $ ^6 v, O. J" E0 i5 ]. Q
    7.     private SingletonTest() {   
      $ z0 D( ]4 m3 ?
    8.     }   ) s+ D1 G3 k8 O. x- j
    9.   
      ; q# d3 N! f6 j" [8 C
    10.     private static final SingletonTest instance = new SingletonTest();   
      ) m: B. E) Z* _
    11.   
      8 v$ U2 V9 j. I4 |
    12.     public static SingletonTest getInstancei() {   
      ' j9 ~  G6 s, K8 J5 e0 Z# ]! Z
    13.         return instance;   
      ' D' m4 j  B* q/ q
    14.     }   : I2 \& K5 k' G1 ~3 ^5 u
    15.   9 f% B6 W- v. D* C4 R1 a
    16. }  </font></font>
    复制代码
    (2)非线程安全: q$ |, ~% ?9 y$ F6 X+ [: ^  T1 ~2 }

    1. <font color="#454545"><font face="Verdana,">/**  4 s3 {. N- t. Y1 l+ a# b
    2. * 单例模式的实现:饱汉式,非线程安全   
      4 s" K( R6 a! W8 q7 R
    3. *   ! s" t( X0 U8 b/ N! X
    4. */  
      2 ^# Y% Q7 y; N, D5 `) e) {0 N1 O  x5 F  q
    5. public class SingletonTest {   / Y, N/ x+ r* T- b* [# E
    6.     private SingletonTest() {   6 C) U$ m6 M( {/ A+ a
    7.     }   . {3 z4 E! K( g* P/ ?8 T  g% N- k
    8.   
      + e; O7 u" M* a& e' ^7 U" z. Z
    9.     private static SingletonTest instance;   & v2 i7 C, p" f
    10.   ) W+ z: v' N4 A# Q/ d' x, F3 o
    11.     public static SingletonTest getInstance() {   : g, x% G- \( n9 g
    12.         if (instance == null)   : T5 _0 B: s* O
    13.             instance = new SingletonTest();   
      ! U2 |  ~7 u1 _* H
    14.         return instance;   
      , ]4 }1 L2 b  ~" g' n, k  W1 e
    15.     }   : g4 B+ c4 Y. {$ `
    16. }</font></font>
    复制代码
    : p3 }1 [+ w" `7 d
    (3)线程安全,但效率比较低
      |' Z7 l& p- c" b

    1. <font color="#454545"><font face="Verdana,">/**
      " I% P" |8 j: j; m5 ?9 O
    2. * 线程安全,但是效率非常低
      & B6 P8 d# ]$ _+ K2 j- f
    3. * @author vanceinfo 2 X/ E% M( H1 K: h
    4. * # b" o3 F; p* P8 n  U: N) D" P( G
    5. */  ) C* K+ k" B) g2 L
    6. public class SingletonTest {  ' ?  e' t7 d! }" _& V! J
    7.     private SingletonTest() {  
      ( t- d; [; m3 ?, H
    8.     }  
      3 t8 a5 z# H$ V; ?% _+ b0 E" x" z
    9.   1 S& v; q! C9 W0 F( S! d
    10.     private static SingletonTest instance;  9 v' C6 \: `" M1 }
    11.   + U1 ]/ O9 b3 F$ x* ^
    12.     public static synchronized SingletonTest getInstance() {  
        b# l8 H4 r% ]. {  F, V, W" u5 T
    13.         if (instance == null)  8 g7 ~) A" I$ g  P& E! O' P0 @# E
    14.             instance = new SingletonTest();  , ~1 G6 x. O3 s; P9 z
    15.         return instance;  5 H+ a+ Z! x8 n! P- `# c/ @
    16.     }  # l9 v+ {: r% }
    17. }</font></font>
    复制代码
    (4)线程安全,并且效率高" X3 l9 r3 z$ b; V

    1. <font color="#454545"><font face="Verdana,">/**  
      " O. `) P5 U( ?0 a: D& u
    2. * 线程安全  并且效率高  
      5 O% A; @- L) x/ F2 S- g0 d
    3. *  
      $ v  [& h1 O" X8 \9 A6 n
    4. */  2 `/ [# T! U, F9 Y% E
    5. public class SingletonTest {   % W7 g" }+ L+ v
    6.     private static SingletonTest instance;   
      ( K' U) g5 g  f& M' a( H- i5 m
    7.   
      6 B' h; ~& _5 Q+ ^( z
    8.     private SingletonTest() {   2 F0 u1 ?. l4 Z% j
    9.     }   
      , B: p" V" M! ^- z  c* Z5 T  }
    10.   
      1 t0 `: c2 h- w* s3 V& C
    11.     public static SingletonTest getIstance() {   ) i  T7 ?  p! S# g- E3 r
    12.         if (instance == null) {   1 z6 U- t* [9 |7 }
    13.             synchronized (SingletonTest.class) {   / `. K/ k% X# C/ ]
    14.                 if (instance == null) {   ( f' r; I* k% u% b" ~5 ~
    15.                     instance = new SingletonTest();   : C* C) f+ Z$ x. Q
    16.                 }   5 M- H' p2 E0 n: G& F8 \
    17.             }   
      / C' _' {6 K  `" n* m1 D
    18.         }   
      % I- J) T0 i" o# E
    19.         return instance;   ( e5 T' Q7 v' O" Z" }" h9 l
    20.     }   
      ( L) s1 g) U" W% L- X& F! }
    21. }</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 [        看看代码示例可能会更清晰,示例代码如下:

    1. public class Singleton {5 _- C! i# r9 f' A
    2.         /**" s. i, W' S& N6 U3 ^- P
    3.          * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例! l" a) j: |6 ^2 D* t5 a$ c
    4.          * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载9 Z- J1 i1 K: V2 U& `. A7 t
    5.          */+ L* o# Q. `! i7 C. [3 |
    6.         private static class SingletonHolder{
      4 D( O- M# W3 h" n- ~" u0 D4 ^- y
    7.                 /**
      ( Y# R- A. y$ \3 {( H
    8.                  * 静态初始化器,由JVM来保证线程安全
      ; G* M$ ], |% p; l; S5 T
    9.                  */
      . O1 d/ y# @( ~- H) P& [) a
    10.                 private static Singleton instance = new Singleton();
      0 J; q$ x' Z, {% o
    11.         }( m5 T" a: Z$ {$ A$ W0 v1 M
    12.         /**
      : U' Z: o2 i: t& y2 p4 B( x
    13.          * 私有化构造方法5 W: D; q+ z4 F& f8 w9 t
    14.          */
      1 c' x* }+ R) i9 v  {
    15.         private Singleton(){( E- `, u+ G9 |
    16.         }
      1 l7 J8 x. ]. O/ H
    17.         public static  Singleton getInstance(){
      6 ^, M1 }6 J0 l* a( Z  {
    18.                 return SingletonHolder.instance;! a# ]' q2 B  q# ~, q7 r* y0 N
    19.         }
      . e: ^9 V7 Y( _4 I4 x4 R6 }4 `2 v
    20. }
    复制代码
            当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$ P
    2 \) 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

    科帮网 1、本主题所有言论和图片纯属会员个人意见,与本社区立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与科帮网享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和科帮网的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、科帮网管理员和版主有权不事先通知发贴者而删除本文


    JAVA爱好者①群:JAVA爱好者① JAVA爱好者②群:JAVA爱好者② JAVA爱好者③ : JAVA爱好者③

    快速回复
    您需要登录后才可以回帖 登录 | 立即注册

       

    关闭

    站长推荐上一条 /1 下一条

    发布主题 快速回复 返回列表 联系我们 官方QQ群 科帮网手机客户端
    快速回复 返回顶部 返回列表