多线程下安全的单例模式(双重校验锁实现,登记者模式实现,利用内部类实现,利用枚举实现)

来源:互联网 发布:老版书旗小说软件 编辑:程序博客网 时间:2024/06/05 10:31

首先我们来看一下什么是懒汉模式:就是实例对象是在你需要的时候才会创建,也就是说当你第一次调用静态方法getInstance() 时它才会帮创你建实例对象。
下面来看一下懒汉模式的线程安全的写法即双重校验锁的懒汉模式

public class TwoCheckSingleInstance {      private static volatile TwoCheckSingleInstance instance;      private TwoCheckSingleInstance(){}      public static TwoCheckSingleInstance getInstatnce(){      //如果不为空可以直接返回,不需要进行加锁处理。这样可以提高效率           if(instance!=null){              return instance;           }          synchronized(TwoCheckSingleInstance.class){              if(instance==null){                  instance=new TwoCheckSingleInstance();              }              return instance;              }         }}

有上面代码可以看出,虽然双重校验可以提高效率,但因为synchronize锁住的还是类对象,是一个类锁。显然这样虽然可以防止多线程环境下出现不安全的情况,但是效率确是比锁住实例对象,即对象锁来的低的多。看到这里有人会说这太简单了吧只要把synchronize(TwoCheckSingleInstance.class)改成synchronize(this)不就行了,如果你有这种想法的话,我只想说,请你回炉重造吧,看清楚这是一个静态方法,所以不可能有this 或super 这种的实例对象引用的。
那问题来了那该怎么获取了,别急,本文的主角登场了。
要实现登记者化的单例模式,我们需要一个登记模板,它是一个通用模板
代码如下

//登记模板,减少重复代码public  abstract class registrantSingleInstance<T> {     private T instance;     //交给子类具体实现     protected abstract  T create();     //多线程下安全的获取单例对象的重复代码     //类似懒汉的双重校验锁,但它锁的是对象,不是整个类,是对象锁,不是类锁     //final 是为了防止子类复写该方法     protected  final T getInstance(){        if (instance!=null){            return instance;        }        synchronized (this) {         if(instance==null){             instance= create();         }             return instance;        }     }}

有了登记模板后,我们就可以利用该模板,来构建我们的单例类

//来访者或者是要单例化的类public class Student {      private Student(){      }      //向登记模板,登记来访者     private static registrantSingleInstance<Student> instance=new registrantSingleInstance<Student>(){        @Override        public Student create() {            return new Student();        }         };     //获取来访者的单例     public static Student  getInstance(){         return instance.getInstance();     }}

我们来看一下性能到底提升了多少

public class SingleTest{    public static void main(String[] args) {         System.out.println("------------------------------>双重校验的懒汉模式");      long start1 =System.currentTimeMillis();           for(int i=0;i<1000000000;i++){              TwoCheckSingleInstance.getInstatnce();           }         System.out.println(System.currentTimeMillis()-start1+"毫秒");         System.out.println("------------------------------>登记者模式");           long start2 =System.currentTimeMillis();           //Student.getStudent();           for(int i=0;i<1000000000;i++){             Student.getInstance();           }         System.out.println(System.currentTimeMillis()-start2+"毫秒");    }}

下面是运行截图,你们测试的时候结果可不一样,当大体上都是,登记者模式的单例比双重校验的快
这里写图片描述
登记者模式的单例,它不仅拥有懒汉的优点,而且还非常简洁,代码复用率就非常高了,特别是要有多个单例化的类时就可以使用登记者模式的单例。如果单例化的类比较少的话,可以用内部类实现的单例,和用枚举实现的单例,这俩种都是利用了类加载机制来保证多线程下的获取单例的安全性。
用内部类实现单例代码如下:

public class InnerSingleInstance {     private InnerSingleInstance(){};   private static class Instance{       private final static InnerSingleInstance instance=new InnerSingleInstance();   }   public static InnerSingleInstance getInstance(){         return Instance.instance;   }}

枚举实现单例代码如下:

public enum EnumSingleInstance {   Instance;   private Tess instance;    private EnumSingleInstance(){        instance=new Tess();    }    public Tess getInstance(){        return instance;    }}

执行效率排行如下:
枚举实现单例 >(快于)用内部实现单例 >(快于) 用登记者模式实现的单例 >(快于)双重校验实现的单例

阅读全文
0 0