标签:字节 不为 调用 设计 public 步骤 静态方法 jvm private
单例模式是Java中常见的一种设计模式,单例模式的写法有好几种,这里主要介绍饿汉式和懒汉式以及懒汉式的改进型。
单例设计模式确保一个类只有一个实例对象,且向所有其他对象提供这一实例。
单利模式的实现步骤:
1、私有化构造函数,避免其他类可以直接创建单例类的对象;
2、在本类中创建唯一的实例对象,使用private static修饰;
3、对外提供一个公开的静态方法,供其他类获取单例类的唯一实例。
饿汉式单例:
class Single{ // 私有化构造函数 private Single(){} // 创建唯一实例 private static Single single= new Single(); // 提供一个公开的获取方法 public static Single getSingle() { return single; } }
饿汉式单例在类加载的时候,创建对象,因此类加载的速度慢,但线程安全。
懒汉式单例:
class Single{ // 私有化构造函数 private Single(){} // 创建唯一实例 private static Single single= null; // 提供一个公开的获取方法,在第一次调用的时候创建对象实例 public static Single getSingle() { if(single == null) { single = new Single(); //创建实例 } return single; } }
懒汉式单例类加载时不会创建对象,调用时才会创建对象,因此类加载速度快,但线程不安全,一般要配合synchronized使用。
懒汉式单例模式优化版(同步方法方式):
class Single{ // 声明一个私有的静态变量 private static Single single=null; // 构造器私有化,避免外部直接创建对象 private Single(){} // 创建一个对外的公共的静态方法访问变量,如果变量没有对象,创建该对象 // 任何线程来的时候都需要等待,等上一个执行完了以后才能进去 public synchronized static Single getSingle(){ if(null==single){ single=new Single(); } return single; } }
synchronized关键字解决了多线程环境下的懒汉式单例模式的线程安全问题,但同时也有个小小的缺憾,就是synchronized关键字开销比较大。可以使用双重检测机制进行优化。
懒汉式单例模式优化版(双重检测机制):
class Single{ //声明一个私有的静态变量 private static Single single=null; //构造器私有化,避免外部直接创建对象 private Single(){} public static Single getSingle(){ // 这样判断两次,可以提高效率 因为a、b进来之后,a执行完,b进入的时候如果single不为空的话直接返回获取对象 // 提高已存在对象的访问效率 if (null==single) { //同步块,因为静态方法中没有this,所以只能锁它的字节码信息 synchronized (Single.class) { if(null==single){ single=new Single(); } } } return single; } }
双重检测机制的第一次检测 if (null==single) 是为了提高代码的执行效率,如果已创建了实例,再次调用getSingle()方法就不必进入同步代码块,不用竞争锁,直接返回已创建的实例;
第二次检测 if (null==single) 可以防止二次创建实例。所以两次检测都必不可少。
双重检测机制理论上是完美的,但实际上仍然有问题,主要原因就是
single=new Single();
这个语句是非原子操作,可以分为三个步骤:
1、为single分配内存;
2、调用构造函数初始化single;
3、将single对象指向分配的内存空间(此时single才不为null);
由于JVM具有指令重排的特性,执行顺序可以是 1 - 2 - 3,也可以是1 - 3 - 2。指令重排在单线程情况下不会出现问题,但多线程情况下会导致一个线程获取到一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用getSingle()方法后发现single不为null,因此返回single,但此时single还没有被初始化。
要解决这个问题需要使用volatile关键字。
private static volatile Single single=null;
volatile关键字会禁止JVM指令重排,从而保证在多线程下正常执行。
标签:字节 不为 调用 设计 public 步骤 静态方法 jvm private
原文地址:https://www.cnblogs.com/cjm09/p/12590253.html