首页 > 代码库 > 设计模式之单列模式
设计模式之单列模式
设计模式之单列模式
1,何为单列模式?
即singleton 在某个类采用了单列模式之后 其只能有一个实列对象 ,并且这个实列对象只能有内部自己创建并提供给外部的调用。
2.实现单列模式的方法
分为 :饿汉式 ,懒汉式
下面为饿汉式实现代码:
public calss Singleton1{ //将构造函数私有化 防止外部通过new来创建对象 private Singleton1(){ } //创建一个私有静态变量并直接初始化 类加载的时候直接创建对象 private static Singleton1 single = new Singleton(); //提供一个public方法 来让外部调用 返回已构建的私有single public static Singleton1 getInstance(){ return single; } }
饿汉式 通过创建一个静态成员变量 在类加载的时候直接创建 该类的对象,所以其天生就是线程安全的。但是缺点在于 无论其是否被使用 内存已经为其分配好内存空间,所以其缺点就是对于有内存要求的饿汉式比较消耗内存。
懒汉式具体实现:
public class Singleton2{ //私有化构造方法 private Singleton2(){ } // 定义静态私有成员变量 但不初始化 private static Singleton2 single = null; //提供public方法给外部用来获取实列对象 public static Singleton2 (){ if(single==null){ single = new Singleton2(); } return single } }
这是懒汉式的基本实现方式,但其对于多线程是不安全的,因为当两个线程同时进入getInstance方法时 在线程1经过single==null判断后 ,对single进行初始化而并未完成初始化,线程2进入single==null判断 由于线程1还未完成初始化 所以此时的single在线程2看来仍然是null,所以线程2也进入了对single进行初始化的代码段,从而出现了两个single对象 ,这与单列模式的要求是不符的,所以为了达到线程安全 下面给出了三种方法,其中第三种方法在某些对单列模式的介绍中 单一分离了出来,但我认为其仍然属于懒汉式,因为在类的加载过程中该类的对象并未被创建,同样是在使用的时候才被创建。
1.通过synchronized同步化 实现线程安全
public calss Singleton3{ //将构造函数私有化 防止外部通过new来创建对象 private Singleton3(){ } //创建一个私有静态变量 占不创建对象 private static Singleton3 single = null; //提供一个public方法 并加上synchronized关键字 同步化来让外部调用 返回已构建的私有single 保证线程安全 public static synchronized Singleton3 getInstance(){
if(single==null){ single=new Singleton3(); }
return single; } }
这种方法虽然实现了线程安全,但是由于synchronized的同步化导致其效率会降低,
2.通过双重锁定实现单列模式的线程安全
public calss Singleton4{ //将构造函数私有化 防止外部通过new来创建对象 private Singleton4(){ } //创建一个私有静态变量 占不创建对象 private static volatile Singleton4 single = null; //提供一个public方法 并加上synchronized关键字 同步化来让外部调用 返回已构建的私有single 保证线程安全 public static Singleton4 getInstance(){ if(single==null){ // 这里面的Singleton4.class可以为任何一个不变的值,它只是一个标识,用来给线程看的 synchronized(Singleton4.class){ if(single==null){ single=new Singleton4(); } } } return single; } }
这种方法实现单列模式 保证了线程的安全性同时提高了效率,而且节约内存空间,只有在第一次调用的时候才会创建对象。
在创建静态变量single时 所加的关键字volatile, 因为构造函数大致可被分为两部分 一是先分配内存并复制 二然后再初始化 所以不加volatile可能导致虽然修改了single的值但是并没有对其初始化 当线程2进入的时候虽然判定条件single==null 为false 而直接返回single,而此时的single 只是开辟了内存空间和赋值而并未被初始化 导致程序运行崩溃; 所以需要加上volatile。
3.通过构造一个静态内部类实现单列模式的线程安全
public class Singleton5{ //私有化构造函数 private Singleton5(){ } //创建一个私有静态内部类 并提供一个静态方法 来创建单一实列对象 private static class Instance{ private static returnInstance(){ // 使用final关键字 让single不可被修改 private static final Singleton5 single =new Singleton5();
} } //提供一个公共方法给外部来获取唯一的对象 public static Singleton5(){ retrun Instance.returnInstance(); } }
这种方法通过静态内部类来实现,由于在类加载的时候 内部类如果没有被使用 其中的single仍然是不会被创建的,只有当外部调用getInstance方法时,它才会被加载从而创建single对象,所以这种方法来实现单列模式的线程安全即保证了在创建该类的实列时只能创建一个,又保证了线程的安全性,而且不会造成资源浪费。
总结
懒汉式和饿汉式的区别
从名字上来看,饿汉式急于创建类的唯一实列,即无论类是否调用,它都会创建好唯一的实列在那,虽然消耗一定的内存空间,但是其调用速度较快,并且实现代码简单。
相对于懒汉式 ,它是先不创建唯一实列,只有在调用getInstance方法时才会创建,所以叫懒汉式。 它基础的实现方式只能适合单一线程使用,其余的三种方式可以保证多线程的使用安全,而这三个方法又各有所长。
对于懒汉式的方法1来说,使用synchronized加锁过大,导致其效率降低影响性能。对于方法2来说使用了双层锁定来实现,比方1的效率高性能要更好,但是其需要加上volatile关键字保证最初进入的两个线程返回的结果一致,而对于volatile关键字涉及到了jvm的底层实现,我本身并不熟悉,所以有待以后加深了解,而且在其他单列模式实现的文章中看到过使用双层锁定的时候通过定义一个临时变量来 替换第二层锁,可以提高25%的效率,其具体的实现和原理也不太了解,有待以后学习。
从以上方法可看到,实现单列模式有以下要素
1.必须将构造方法私有化,防止外部创建对象
2.唯一的实列对象只能通过内部来创建。
3.必须要提供一个public方法让外部能够获得这个唯一的实列对象。
设计模式之单列模式