设计模式(3)---单例模式

单例模式 Singleton (创建型模式)

 

1.定义

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。

 

2.结构图

 

3.代码

懒汉式

 1 /*
 2  * 懒汉式
 3  * 实现了延迟创建和保证了线程安全
 4  */
 5 public class Singleton {
 6 
 7     private static Singleton instance = null ;
 8     
 9     private Singleton() {
10         
11     }
12     
13     public static synchronized Singleton getInstance(){
14         if (instance == null){
15             instance = new Singleton() ;
16         }
17         return instance ;
18     }
19 
20 }

 

双重检查加锁

synchronized对整个方法加锁是没必要的,只要保证实例化对象的那段代码不被多线程同时访问就行了,当两个线程同时访问这个方法时,假设这时对象还没有被实例化,他们都可以通过第一重instance==null的判断,然后由于lock机制,这两个线程只有一个能进入,如果没有第二重的判断,则第一个线程创建了实例后,第二个线程还是可以继续再创建新的实例。

 1 /*
 2  * 双重检查加锁
 3  * 
 4  */
 5 public class Singleton {
 6 
 7     private volatile static Singleton instance = null ;
 8     
 9     private Singleton() {
10         
11     }
12     
13     public static  Singleton getInstance(){
14         if (instance == null){
15             synchronized(Singleton.class){
16                 if (instance == null){
17                     instance = new Singleton() ;
18                 }
19             }    
20         }
21         return instance ;
22     }
23 
24 }


Instance 采用 volatile 关键字修饰也是很有必要的。

Instance = new Singleton(); 这段代码其实是分为三步执行。

  1. 分配内存空间。
  2. 初始化对象。
  3. 将 Instance 指向分配的内存地址。

但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程就有可能 B 线程获得是一个还没有被初始化的对象以致于程序出错。

所以使用 volatile 修饰的目的是禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。



采用静态内部类的形式
Java中静态内部类可以访问其外部类的成员属性和方法,同时,静态内部类只有当被调用的时候才开始首次被加载,利用此特性,可以实现懒汉式,在静态内部类中静态初始化外部类的单一实例即可。
既是线程安全的,同时又提升了性能
 1 /*
 2  * 懒汉形式改进版   采用了静态内部类
 3  * 
 4  */
 5 public class Singleton {
 6 
 7     private Singleton() {
 8         
 9     }
10     private static class LazyHolder{
11         private static final Singleton instance = new Singleton();
12     }
13     
14     public static  Singleton getInstance(){
15         return LazyHolder.instance ;
16     }
17 
18 }

 

或者

 1 /**
 2  * 请求内存队列
 3  * @author Administrator
 4  *
 5  */
 6 public class RequestQueue {
 7     
 8     /**
 9      * 单例有很多种方式去实现:我采取绝对线程安全的一种方式
10      * 
11      * 静态内部类的方式,去初始化单例
12      * 
13      * @author Administrator
14      *
15      */
16     private static class Singleton {
17         
18         private static RequestQueue instance;
19         
20         static {
21             instance = new RequestQueue();
22         }
23         
24         public static RequestQueue getInstance() {
25             return instance;
26         }
27         
28     }
29     
30     /**
31      * jvm的机制去保证多线程并发安全
32      * 
33      * 内部类的初始化,一定只会发生一次,不管多少个线程并发去初始化
34      * 
35      * @return
36      */
37     public static RequestQueue getInstance() {
38         return Singleton.getInstance();
39     }
40     
41 
42     
43 }
饿汉形式

 1 /*
 2  * 饿汉形式
 3  * 
 4  */
 5 public class Singleton {
 6 
 7     private static final Singleton instance = new Singleton() ;
 8     
 9     private Singleton() {
10         
11     }
12     
13     public static  Singleton getInstance(){
14         return instance ;
15     }
16 
17 }

 

 

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

 

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

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

原文地址:https://www.cnblogs.com/mengchunchen/p/5727627.html