设计模式之单例模式

介绍

单例模式即在整个系统应用中存在唯一的对象实例。通过将构造函数私有化,且基本上只提供唯一的静态方法来进行访问。

属于创建型模式。

七种方式

单例模式大致分为懒汉式,饿汉式,内部类,枚举这几种,再根据线程是否安全细分为7种。

下面将详细列举出这7种:

1.静态常量饿汉式(线程安全)

public class Singleton {
    //构造方法私有
   private Singleton(){}
   //创建对象实例
   private final static Singleton singleton=new Singleton();
   //获得对象
    public Singleton getSingleton(){
        return singleton;
    }
}

此种方式的优点就是简单,并且在类加载的时候就创建了对象,避免了线程安全导致的问题。缺点就是一旦某种原因进行了类加载就会创建对象,假如说系统不会用到此对象,就会造成内存浪费。

2.静态代码块饿汉式(线程安全)

public class Singleton {
    //构造方法私有
   private Singleton(){}
   //声明对象
   private static Singleton singleton;
   //静态代码块
   static {
       singleton=new Singleton();
   }
   //获得对象
    public Singleton getSingleton(){
        return singleton;
    }
}

此种方式的优缺点与上面的静态常量方式几乎相同,都是在类加载的时候进行实例化(类加载的时侯会对静态变量和静态代码块进行初始化)

3.线程不安全懒汉式

public class Singleton {
    //构造方法私有
   private Singleton(){}
   //声明对象
   private static Singleton singleton;
   //获得对象
    public Singleton getSingleton(){
        if (singleton==null)
            singleton=new Singleton();
        return singleton;
    }
}

此方式优点就是实现了懒加载,只有在用到的时候进行对象的实例化,避免了内存浪费。

缺点是线程不安全,假如说在执行到if(singleton==null)这句代码,第一个线程执行了这句代码判断为true后还未来得及往下执行,第二个线程也执行了这段代码,这样就会new两次创建两个对象。

4.线程安全懒汉式

public class Singleton {
    //构造方法私有
   private Singleton(){}
   //声明对象
   private static Singleton singleton;
   //获得对象
    public synchronized Singleton getSingleton(){
        if (singleton==null)
            singleton=new Singleton();
        return singleton;
    }
}

在线程不安全懒汉式方式的基础上,往获得对象的方法上加synchronized修饰符,这样保证了线程安全,但是效率太低了,每次获得对象都要进行加锁操作,在实际开发中几乎不使用这种方法。

5.DoubleCheck(重要)

public class Singleton {
    //构造方法私有
   private Singleton(){}
   //声明对象
   private static volatile Singleton singleton;
   //获得对象
    public Singleton getSingleton(){
        if (singleton==null)
            synchronized (Singleton.class){
                if (singleton==null)
                    singleton=new Singleton();
            }
        return singleton;
    }
}

双重检查方式,即保证了效率,又实现了懒加载,还保证了线程安全,是非常推荐的一种方式,也是面试常考的方式。

大家可能有点奇怪,这里怎么用了两个判空操作?我不是都加synchronized了?为什么用volatile修饰singleton?

这个地方其实很奇妙,还是按照上次的例子:假如我这里没有第二个判空操作,当第一个线程执行了第一个判空操作后进入下面代码还未执行,第二个线程也进来判空,就无法保证线程安全。当有第二个判空操作时,先进来的线程进入同步代码块实例化了对象,而由于同步代码块的存在,后来的线程先在外面等着,等第一个线程执行完后再进入,此时它再判空后就得到了false,不再实例化对象。保证了线程安全。volatile在这里的作用是防止指令重排,保证代码正常的执行顺序。

6.静态内部类(线程安全)

public class Singleton {
    //构造方法私有
   private Singleton(){}
   //声明内部类
   private static class SingletonInstance{
       private final static Singleton singleton=new Singleton();
    }
   //获得对象
    public Singleton getSingleton(){
        return SingletonInstance.singleton;
    }
}

内部类的一个特点:当外部类进行类加载时,内部类并不会被加载。

当只有调getSingleton()方法时SingletonInstance才会类加载。所以静态内部类方式可以实现懒加载,且加载的时候是线程安全的,且类加载只一次,这样就实现了线程安全和单例。

7.枚举(线程安全)

enum  Singleton {
  SINGLETON
}

JDK1.5加入的枚举,让代码简单到让人不可思议!

枚举可以解决上述所有问题,还能防止反射创建对象,太强大了!

posted @ 2020-05-24 23:09  网络小渣渣  阅读(57)  评论(0)    收藏  举报