保证一个类仅有一个实例,并提供一个访问它的全局访问点。编程
一般咱们可让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的惟一实例。这个类能够保证没有其余实例能够建立,而且它能够提供一个访问该实例的方法。设计模式
/** * Created by callmeDevil on 2019/8/17. */ public class Singleton { private static Singleton instance; private Singleton(){} //构造方法私有,防止外界建立实例 // 得到本类实例的惟一全局访问点 public static Singleton getInstance(){ if (instance == null) { //若实例不存在,则建立一个新实例,不然直接返回已有实例 instance = new Singleton(); } return instance; } }
public class Test { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); if (s1 == s2) { System.out.println("两个对象是相同的实例"); } } }
两个对象是相同的实例
在没有并发问题的状况下,这种方式也是使用比较多的。但缺点也很明显,多线程下根本无法用。服务器
/** * Created by callmeDevil on 2019/8/17. */ public class SingletonOnLock { private static SingletonOnLock instance; private SingletonOnLock(){} public static SingletonOnLock getInstance(){ // 同步代码块,只有一个线程能进入,其余阻塞 synchronized (SingletonOnLock.class){ if(instance == null){ instance = new SingletonOnLock(); } } return instance; } }
当存在对象实例时,彻底不用担忧并发时致使堆中建立多个实例,但每次调用 getInstance() 方法时都被加锁,是会影响性能的,所以这个类能够继续改良。多线程
/** * Created by callmeDevil on 2019/8/17. */ public class SingletonOnDoubleCheckLock { private static SingletonOnDoubleCheckLock instance; private SingletonOnDoubleCheckLock(){} public static SingletonOnDoubleCheckLock getInstance(){ // 先判断实例是否存在,不存在再考虑并发问题 if (instance == null) { synchronized (SingletonOnDoubleCheckLock.class){ if(instance == null){ instance = new SingletonOnDoubleCheckLock(); } } } return instance; } }
当实例存在时,就直接返回,这是没有问题的。当实例为空而且有两个线程调用 getInstance() 方法时,它们均可以经过第一重 instace == null 的判断,而后因为 synchronized 机制,只有一个线程能够进入,另外一个阻塞,必需要在同步代码块中的线程出来后,另外一个线程才会进入。而此时若是没有第二重的判断,那第二个线程仍然会建立实例,这就达不到单例的目的了。并发
但这种方式是最让人“诟病”的一种不推荐方式,技巧看上去很好,但实际上一样影响性能。性能
/** * 该类声明为final ,阻止派生,由于派生可能会增长实例 * Created by callmeDevil on 2019/8/17. */ public final class SingletonStatic { // 第一次引用类的任何成员时就建立好实例,同时没有并发问题 private static final SingletonStatic instance = new SingletonStatic(); private SingletonStatic(){} public static SingletonStatic getInstance(){ return instance; } }
JVM第一次加载类的时候就已经建立好了实例,若是接下来的很长时间都没有用到的话,占用的内存至关于被浪费了,也不是最让人推荐的一种方式。固然如今的服务器容量也愈来愈大,单单一个实例的内存也并非任何状况都要考虑节省。除非追求极致。。测试
/** * Created by callmeDevil on 2019/8/17. */ public class SingletonStaticClass { private SingletonStaticClass() {} public SingletonStaticClass getInstande() { return InterClass.instance; } // 静态内部类,没有并发问题 private static final class InterClass { public static SingletonStaticClass instance = new SingletonStaticClass(); } }
JVM第一次加载外部的 SingletonStaticClass 时,并不会直接实例化,因此这种方式也属于“懒汉式”。只有在第一次调用 getInstance() 方法时,JVM才会加载内部类 InterClass,接着才实例化静态变量,也就是咱们须要的外部类的单例。这样不只延时了实例化,同时也解决了并发访问的问题,所以该方式是最为推荐的一种方式。线程