Java单例类

            单例类:

主要知识点:

1,单例类概念、特点

2,三种单例类懒汉,饿汉,双重加锁举例,

3,懒汉、饿汉区别以及单例类的总结;

1,概念:
  java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、双重检查加锁单例三种。
  单例模式有以下特点:
  1、单例类只能有一个实例。//构造函数为private
  2、单例类必须自己创建自己的唯一实例。
  
3、单例类必须给所有其他对象提供这一实例。//public方法

2,三种单例类

饿汉式:

 1 public class EagerSingleton {
 2 
 3     private static EagerSingleton instance = new EagerSingleton();
 4 
 5     /**
 6 
 7      * 私有默认构造子
 8 
 9      */
10 
11     private EagerSingleton(){}
12 
13     /**
14 
15      * 静态工厂方法
16 
17      */
18 
19     public static EagerSingleton getInstance(){
20 
21         return instance;
22 
23     }
24 
25 }

        例子中,在这个类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用。这时候,单例类的唯一实例就被创建出来了。饿汉式在装载类的时候就创建对象实例。

饿汉式是空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。

懒汉式:

 1 public class LazySingleton {
 2 
 3     private static LazySingleton instance = null;
 4 
 5     /**
 6 
 7      * 私有默认构造子
 8 
 9      */
10 
11     private LazySingleton(){}
12 
13     /**
14 
15      * 静态工厂方法
16 
17      */
18 
19     public static synchronized LazySingleton getInstance(){
20 
21         if(instance == null){
22 
23             instance = new LazySingleton();
24 
25         }
26 
27         return instance;
28 
29     }
30 
31 

 懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。懒汉式在不急着创建对象实例。会一直等到马上要使用对象实例的时候才会创建,在装载对象的时候不创建对象实例。
 

 懒汉式是时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间

 由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。

 

双重检查加锁:(java5及以上的版本)

            所谓“双重检查加锁”机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。使用“双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受很大的影响

   “双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

 1 public class Singleton {
 2 
 3     private volatile static Singleton instance = null;
 4 
 5     private Singleton(){}
 6 
 7     public static Singleton getInstance(){
 8 
 9         //先检查实例是否存在,如果不存在才进入下面的同步块
10 
11         if(instance == null){
12 
13             //同步块,线程安全的创建实例
14 
15             synchronized (Singleton.class) {
16 
17                 //再次检查实例是否存在,如果不存在才真正的创建实例
18 
19                 if(instance == null){
20 
21                     instance = new Singleton();
22 
23                 }
24 
25             }
26 
27         }
28 
29         return instance;
30 
31     }
32 
33 }

  这种实现方式既可以实现线程安全地创建实例,而又不会对性能造成太大的影响。它只是第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。

  提示:由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。

 

3,饿汉式和懒汉式区别以及总结

两种区别主要两点

1、线程安全:

饿汉式是线程安全的,可以直接用于多线程而不会出现问题,懒汉式就不行,它是线程不安全的,如果用于多线程可能会被实例化多次,失去单例的作用。

如果要把懒汉式用于多线程,有两种方式保证安全性,一种是在getInstance方法上加同步,另一种是在使用该单例方法前后加双锁。

2、资源加载:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,会占据一定的内存,相应的在调用时速度也会更快,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次掉用时要初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

 

结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

 

附:

线程安全:

  如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。