何为单例?哪些类适合做成单例?做成单例的好处又是什么呢?如何实现单例?
概念
何为单例?
定义:单例模式(Singleton)是保证一个类仅有一个实例并提供一个访问它的全局访问点。
定义中就能看到,要保证一个类仅有一个实例化的对象。要如何保证呢?最好的办法就是让它自身负责保存它唯一的实例,而且这个类不能被除自身以外的类实例化。如图:
场合
哪些类适合做成单例?
实际工作中,总有一些类的不同对象都担负着相同的任务,而且当新的对象出现后,旧的的对象就会被舍弃或者可以被新的对象所替代。为什么可以被替代呢,因为无论这个类实例化多少对象,其状态都是一样的。还有一种情况,如果某个类实例化多个会出现程序异常或逻辑错误,那么这些类都可以做成单例。总结一下:
2.同一时间对象状态一致。
3.多个对象会导致异常或逻辑错误。
4.对象创建非常耗费资源又经常使用。
功能
做成单例的好处又是什么呢?
2.减少逻辑错误,易于管理。
3.避免单例类对共享资源的多重占用已经操作资源时导致性能下降或损耗(如日志文件、应用配置)。
4.方便资源之间相互通信(如线程池)。
实现
根据定义,便有了第一种实现方法:
// 1.懒汉式(线程不安全)
public class Singleton { // 定义一个变量保存自身唯一实例 private static Singleton instance; // 定义一个私有构造方法防止其他类实例化 private Singleton() {} // 定义公共静态方法创建实例 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
这是最标准也是最原始的实现方式,但也是在不考虑线程安全的实现方式。如果同时有多个线程同时调用 getInstance()
就会导致在创建了多个实例。
为了达到并发状态下线程安全的目的,故而做了如下改动:
// 2.懒汉式(线程安全,效率低下)
public class Singleton { // 定义一个变量保存自身唯一实例 private static Singleton instance; // 定义一个私有构造方法防止其他类实例化 private Singleton() { } // 定义公共静态方法创建实例 public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
其实,只是给 getInstance()
以关键字 synchronized
给此方法加锁,使得此方法线程同步。然而这种做法导致多出了许多无谓的线程等待,不推荐使用。为了避免过多等待,渐渐有了下面这种实现方式:
// 3.双重检测(线程安全,提升效率)
public class Singleton { // 定义一个变量保存自身唯一实例 private static Singleton instance; // 定义一个私有构造方法防止其他类实例化 private Singleton() { } // 定义公共静态方法创建实例 public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
首先判断其是否被实例化,再同步实例化代码,提升了性能。
由于static变量在内存中始终保持一个引用,这是JVM自身决定的,所以也可以用内部类的方式实现:
// 4.静态内部类(线程安全)
public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHelp.instance; } private static class SingletonHelp { static Singleton instance = new Singleton(); } }
既然如此,那为何不直接以类变量初始化呢?如下:
// 5.饿汉式(线程安全)
public class Singleton { // 定义一个变量保存自身唯一实例 private static Singleton instance = new Singleton(); // 定义一个私有构造方法防止其他类实例化 private Singleton() { } // 定义公共静态方法创建实例 public static Singleton getInstance() { return instance; } }
所谓饿汉式,就是如果访问了Singleton其他任何静态域,就会造成实例初始化,即时从来没用过此实例也会造成内存的浪费。在不计较内存的情况下,推荐使用此种方式,毕竟天生线程安全嘛!
到此,单例模式常用的实现方式便完成了。