单例模式--独一无二的对象

1.单例模式的定义

单例模式(Singleton Pattern):用来建立独一无二的,只能有一个实例的对象的入场券。html

在咱们进行开发的时候,有些对象咱们只须要一个,好比:配置文件,工具类,线程池、缓存、日志对象等。若是创造出多个实例,就会致使许多问题,好比占用资源过多,不一致的结果等。使用单例模式就能保证在程序中须要的实例只有一个。编程

单例模式的类型:懒汉模式、饿汉模式。缓存

 

2.单例模式的类图

下面是单例模式的类图:安全

咱们将建立一个单例对象类 - Singleton。单例对象(Singleton)类的构造函数是私有的,而且具备自身的静态实例。多线程

Singleton类提供了一个静态方法来获取其静态实例到外部世界。SingletonTest或示例类将使用Singleton类提供的静态方法来获取Singleton对象。yii

3.单例模式--饿汉式

饿汉式是在建立自身的静态实例的时候就直接实例化单例对象,而后在提供的静态方法中直接返回这个实例对象。函数

 1 /**
 2  * 单例模式(饿汉模式)
 3  * @author admin
 4  *
 5  */
 6 public class Singleton {
 7     //1.将构造方法私有化,不容许外部直接建立对象
 8     private Singleton(){
 9         }
10     
11     //2.建立类的惟一实例,使用private,static修饰
12     private static Singleton instance = new Singleton();
13     
14     //3.提供一个用于获取实例的方法,使用public static 修饰
15     public static Singleton getInstance(){
16         return instance;
17     }
18 }    

4.单例模式-懒汉式

懒汉式在建立自身的静态实例的时候不直接实例化单例对象,只有在外部调用获取单例对象的静态方法的时候才会判断单例对象是否已经存在,若是 不存在则建立一个并返回,若是已经存在则直接返回。工具

 1 /**
 2  * 单例模式(懒汉模式)
 3  * @author admin
 4  */
 5 public class Singleton2 {
 6     //1.将构造方法私有化,不容许外边直接建立对象
 7     private Singleton2(){
 8     }
 9     //2.声明类的惟一实例,使用private,static修饰,可是此处不实例化
10     private static Singleton2 instance;
11     //3.提供一个用于获取实例的方法,使用public static修饰
12     public static Singleton2 getInstance(){
13         if (instance == null) {
14             instance = new Singleton2();
15         }
16         return instance;
17     }
18 }

 

 

5.测试

 1 /**
 2  * 单例模式的测试类
 3  * @author admin
 4  *
 5  */
 6 public class SingletonTest {
 7     public static void main(String[] args) {
 8         //饿汉模式
 9         Singleton s1 = Singleton.getInstance();
10         Singleton s2 = Singleton.getInstance();
11         
12         if (s1 == s2) {
13             System.out.println("s1和s2是同一个对象");
14         }else {
15             System.out.println("s1和s2不是同一个对象");
16         }
17         
18         //懒汉模式
19         Singleton2 s3 = Singleton2.getInstance();
20         Singleton2 s4 = Singleton2.getInstance();
21         if (s3 == s4) {
22             System.out.println("s3和s4是同一个对象");
23         }else {
24             System.out.println("s3和s4不是同一个对象");
25         }
26     }
27 }

 

测试结果:性能

从测试结果能够看出来,获取的对象是同一个对象,也就是说,返回的是单例对象。测试

6.饿汉式和懒汉式的区别

  1. 饿汉式是在单例类加载的时候就初始化单例对象,因此在加载类的时候速度比较慢,可是在运行时的速度比较快,同时是线程安全的(线程安全问题下面再详细讲解)。
  2. 懒汉式在单例类加载的时候只定义单例对象,不进行初始化,因此加载类的时候速度比较快,可是在运行时须要进行单例对象的初始化,因此在运行时速度比较慢,并且是线程不安全的。

7.单例模式在多线程中的问题

上面的代码在普通的应用程序中没有任何任何问题,可是在多线程中使用的时候就会发现,返回的单例对象并非惟一的,并且多个不一样的单例对象,这就说明在多线程中,单例模式产生的单例对象并非"惟一"的。要解决这个问题,有3种办法(其中两种方法都是针对懒汉式的),这3种方法在解决多线程问题的同时也有本身的缺点:

  • 第一种方法:使用"急切"的建立实例,而不用延迟实例化的作法。若是程序老是建立并使用单例实例,或者在建立和运行时方面的负担不太繁重,那就可使用急切(eagerly)建立此单例实例。其实这就是饿汉式单例模式。利用这个作饭,咱们依赖JVM在加载这个类是立刻建立此惟一的单例实例。JVM保证在任何线程访问单例静态变量以前,必定先建立此单例实例。
  • 第二种方法(此方法是针对懒汉式单例模式):把静态类中的静态方法(上面代码中的getInstance())方法编程同步(synchronized)方法,多线程问题几乎就能够轻易的解决了。它的缺点是下降了性能,并且可能比这还更严重一些,经过单例模式的定义咱们知道,单例对象只有在该方法第一次执行时,才真正须要同步,在之后的调用中,该单例对象已经存在了,因此并不须要再次进行实例化。也就是说,当设置好instance这个变量后,就再也不须要同步这个方法了,以后的每次调用这个方法,同步都是一种累赘。并且,同步一个方法可能形成程序执行效率降低100倍,所以,若是在频繁运行的地方调用该方法,那么就得好好考虑这个问题了。固然,若是getInstance()方法的性能对应用程序不是很关键,那么能够忽略同步带来的问题。
  • 第三种方法(此方法也是针对懒汉式单例模式):用"双重检查加锁",在getInstance()方法中减小使用同步。利用双重检查加锁(double-checked locking),首先检查是否单例实例是否已经建立了,若是还没有建立,"才"进行同步。这样一来,只有第一次会同步,这正是咱们想要的。将上面懒汉式的代码改为以下:
     1 /**
     2  * 单例模式(懒汉模式)
     3  * @author admin
     4  */
     5 public class Singleton2 {
     6     //1.将构造方法私有化,不容许外边直接建立对象
     7     private Singleton2(){
     8         
     9     }
    10     //2.声明类的惟一实例,使用private,static修饰,可是此处不实例化
    11     private volatile static Singleton2 instance;
    12     //3.提供一个用于获取实例的方法,使用public static修饰
    13     public static Singleton2 getInstance(){//检查实例,若是不存在就进入同步区块
    14         if (instance == null) {
    15             synchronized (Singleton2.class){//注意,只有第一次才完全执行这里的代码
    16                 if (instance == null) {//进入区块后,再检查一次,若是仍是null,才建立实例
    17                     instance = new Singleton2();
    18                 }
    19             }
    20         }
    21         return instance;
    22     }
    23 }

     volatile关键字确保:当单例变量被初始化成Singleton实例时,多个线程正确地处理单例变量。若是性能问题是关注的重点,那么这个方法能够大大地减小getInstance()的时间消耗。可是该方法也有其缺点,那就是双重检查加锁不适用于1.4及更早版本的Java。在1.4及其更早的版本中,许多JVM对于volatile关键字的实现会致使双重检查加锁的实效。若是不能使用Java5以上的版本,而必须使用旧版本,那么该方法就没法解决多线程的问题。

好了单例模式的叙述到此就结束了,若是有什么讲解的不正确的地方,欢迎你们多多指教!

文章部份内容引用自以下地址:http://www.yiibai.com/design_pattern/singleton_pattern.html

相关文章
相关标签/搜索