设计模式之单例模式

1、定义:保证每个类仅有一个实例,并给外部提供一个访问它的全局访问点。

(1)设计思路:如果一个类能够被创建多个实例,那么这个类的构造方法肯定是公开的。若把创建实例对象的权限收回来,让类自身负责创建实例,然后由类本身来提供外部访问这个类的实例的方法,就实现了单例模式。

(2)实现单例模式的方法,懒汉式和饿汉式。区别在于具体创建对象实例的处理上

(3)懒汉式

 1 1 /**
 2  2  * 懒汉式
 3  3  */
 4  4 public class Singleton {
 5  5 
 6  6     //存储创建好的实例对象
 7  7     private static Singleton uniqueInstance = null;
 8  8 
 9  9     //私有化构造方法
10 10     private Singleton(){}
11 11 
12 12     //为外部提供类实例
13 13     public static synchronized Singleton getInstance(){
14 14         //判断变量是否有值
15 15         if (uniqueInstance == null){
16 16             //没有值,就创建对象并赋值
17 17             uniqueInstance = new Singleton();
18 18         }
19 19         //有值就直接使用
20 20         return uniqueInstance;
21 21     }
22 22 }

  1)懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间,由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式实现呢?

  2)双重检查加锁机制:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

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

  4)代码如下:

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

(4)饿汉式

1 /**
 2  * 饿汉式
 3  */
 4 public class Singleton {
 5 
 6     private static Singleton uniqueInstance = new Singleton();
 7 
 8     //私有化构造方法
 9     private Singleton(){}
10 
11     //定义一个方法来为客户端提供类实例
12     public static  Singleton getInstance(){
13         return uniqueInstance;
14     }
15 }

2、单例模式详解:

  保证类在运行期间只会创建一个实例,并提供了一个全局唯一的访问这个类的访问点,就是代码中的getInstance()方法。不管懒汉式还是饿汉式,这个访问点是一样的。对单例模式本身而言,它只关心类实例的创建问题,并不关心具体的业务问题。

  目前Java里面实现的单例是一个ClassLoader及其子ClassLoader的范围,因为一个ClassLoader在装载饿汉式单例的时候,就会创建一个类的实例。如果一个虚拟机里面有多个ClassLoader,而这些ClassLoader都装载某个类的话,就算这个类是单例类,也会产生很多个实例。如果一个机器上有多个虚拟机,那么每个虚拟机里都应该至少有一个这个类的实例,整个机器上有多个实例,就不再是单例了。

  注意:不加同步的懒汉式是线程不安全,饿汉式是线程安全的,因为虚拟机保证了只会装载一次,在装载类的时候是不会发生并发的。

posted on 2020-03-12 20:44  hdc520  阅读(142)  评论(0编辑  收藏  举报

导航