单例模式:采用必定的方法,使得软件运行中,对于某个类只能存在一个实例对象,而且该类只能提供一个取得实例的方法。java
分类:安全
实现思路:多线程
想要实现单例,即不能让外部随意的去实例化对象。因此须要构造器私有线程
既然不容许外部去建立了,因此须要在类的内部建立对象code
外部须要使用对象,因此须要对外提供一个获取实例的方法对象
根据对象建立的时机,能够分为饿汉式和懒汉式。饿汉式,即在类加载的时候当即建立实例,根据所学知识咱们能够很快想到要使用static
关键字将属性和类相关联。如下提供两种书写方式供参考。进程
特色内存
class Singleton01{ //构造器私有,防止外部new private Singleton01(){ } //内部建立对象 private final static Singleton01 instance = new Singleton01(); //提供给外部建立实例的静态方法 public static Singleton01 getInstance(){ return instance; } }
class Singleton02{ //构造器私有,防止外部new private Singleton02(){ } //内部建立对象 private static Singleton02 instance; static { instance = new Singleton02(); } //提供给外部建立实例的静态方法 public static Singleton02 getInstance(){ return instance; } }
这种方式和静态常量的实现方式逻辑和优缺点是同样的,只是写法不一样。资源
懒汉式,即在类加载时并不实例化对象,等到使用对象实例的时候才去实例化,也被称为懒加载效果。这样作的好处能够节约资源,减小浪费,只有须要的时候采起建立,不须要就不会实例化。get
class Singleton01{ // 构造器私有 private Singleton01(){ } // 定义静态变量,实例化留在获取实例的getInstance方法,起到懒加载效果 private static Singleton01 instance; public static Singleton01 getInstance(){ // 判断若是为空才建立,起到懒加载 if (instance == null){ instance = new Singleton01(); } return instance; } }
问题:在多线程状况下,假设类还未第一次实例化,此时两个进程同时执行到了if(instance==null)
,而将来得及往下执行时,此时二者校验都成立,都会执行实例化操做,将有可能出现建立多个实例的问题。
既然存在线程安全问题,确定会想到使用synchronized
关键字来解决
class Singleton02{ private static Singleton02 instance; private Singleton02(){ } //使用synchronized关键字来实现线程安全 public static synchronized Singleton02 getInstance(){ if (instance == null){ instance = new Singleton02(); } return instance; } }
这样的方式能够起到线程安全的效果,可是每一个线程都须要等待锁,因此又会存在效率低的问题,因而有人想到了将锁的范围缩小到方法的内部,使用同步代码块的方式
这样的方式好很差呢?先看代码
class Singleton03{ private static Singleton03 instance; private Singleton03(){ } public static Singleton03 getInstance(){ if (instance == null){ // 这里的synchronized其实没有实际意义,可能会产生多个实例 synchronized (Singleton03.class){ instance = new Singleton03(); } } return instance; } }
这样锁的范围是变小了,可是还会存在多个线程同时判断到if (instance == null)
,即便在后面加上锁,依旧会在后续建立实例,只是延迟了一点而已,因此这种写法不可取
为了可以实现懒加载的效果,同时兼顾效率,因而出现了这种写法
class Singleton01{ //volatile,当有发生变化时即时储存到内存中。防止指令重排 private static volatile Singleton01 instance; private Singleton01(){ } //双重检查,解决线程同步问题,又保证效率 public static Singleton01 getInstance(){ if (instance == null){ // 第一次检查,下降产生锁的几率 synchronized (Singleton01.class){ if(instance == null){ // 第二次检查,保证线程安全 instance = new Singleton01(); } } } return instance; } }
使用双重检查,第一次检查提高效率,第二次检查保证线程安全,简直美滋滋
利用静态内部类在被调用时才会加载,即存在懒加载效果,因此也能够这样写
class Singleton02{ private Singleton02(){ } /* 静态内部类在外部类装载的时候不会立刻执行,起到懒加载做用。 类的静态属性只有在第一次使用的时候才会加载,JVM在类加载时是线程安全的 */ private static class SingletonInstance{ private static final Singleton02 INSTANCE = new Singleton02(); } public static Singleton02 getInstance(){ return SingletonInstance.INSTANCE; } }
枚举方式是最简单的写法,也是被不少人推崇的写法
enum Singleton03{ INSTANCE; }
简单明了...
使用单例模式,可使一个类只存在一个实例对象,从而节省了系统资源。
上文中列出了8个写法,其中懒加载的写法存在线程安全和效率的问题,须要谨慎使用。比较推荐的写法有5种:懒加载2种+其余方式3种。当认定单例的对象在软件中必定会用到,可使用懒加载,反之可使用其余方式