一、概述
1.1 特点
- 单例类。
只有一个实例对象
; - 该单例对象必须由单例类自行创建
- 单例类对外提供一个访问该单例的
全局访问点
。
1.2 优缺点
优点:
- 保证内存里只有一个实例,减少了
内存的开销
。- 可以避免对资源的多重占用
缺点:
- 没有接口,扩展困难。违背
开闭原则
。- 不利于代码调试。调试过程中,如果单例代码没有执行完,不能创建一个对象
1.3 应用场景
频繁创建类
,使用单例可以降低系统的内存压力,减少GC。全局信息类
。利用一个类记录网站的访问次数,不希望有的访问被记录在对象A上,有的记录在对象B上。
二、八种经典实现方式
2.1 饿汉式
代码实现:
public class Hungry {
private Hungry(){};
// 1. 静态变量
// private static Hungry hungry = new Hungry();
// 2. 静态代码块 private final static Hungry hungry;
static { hungry = new Hungry(); }
public static Hungry getInstance() { return hungry; }
}
优点: 类加载时,就把实例初始化。
缺点: 浪费内存空间。
2.2 懒汉式
代码实现:
// 1. 线程不安全
// 2. 线程安全,synchronized修饰方法
// 3. 线程安全,synchronized代码块修饰
public class Lazy {
private Lazy(){
}
// 1. 多线程不安全
private static Lazy lazy = null;
public Lazy getInstance() {
if (lazy == null) {
lazy = new Lazy();
}
return lazy;
}
// 2. 线程安全,synchronized修饰方法。多线程无法并行处理,效率低下
public synchronized Lazy getInstance() {
if (lazy == null) {
lazy = new Lazy();
}
return lazy;
}
// 3. 线程安全,synchronized代码块修饰. 产生多个实例,即多个线程都通过lazy==null的判断
public static Lazy getInstance(){
if (lazy == null) {
synchronized (Lazy.class){
lazy = new Lazy();
}
}
return lazy;
}
适用场景: 如果程序一开始要加载的资源太多,可以使用懒加载。
2.3 双重检查方式
代码实现:
// 1. 双重检测1:线程安全,效率较高,延迟加载;
// 但存在指令重排,即创建对象需要3个步骤,3个步骤之间可能会重排,导致空指针
// 异常
// 2. 双重检测2:利用volatile修饰的变量;volatile可以防止指令重排
public class DoubleCheck {
private DoubleCheck(){
}
// 1. 双重检测1
// private static DoubleCheck doubleCheck;
// 2. 双重检测2
private volatile static DoubleCheck doubleCheck;
public static DoubleCheck getInstance() {
if (doubleCheck == null) {
synchronized (DoubleCheck.class) {
if (doubleCheck == null) {
doubleCheck = new DoubleCheck();
}
}
}
return doubleCheck;
}
}
优点:线程安全;延迟加载;效率较高
2.4 静态内部类方式
public class Inner {
private Inner(){
}
public static Inner getInstance() {
return InnerClass.inner;
}
private static class InnerClass{
private static final Inner inner = new Inner();
}
}
静态内部类方式在Inner类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载
InnerClass类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化
时,别的线程是无法进入的。
【优点】:避免了线程不安全,延迟加载,效率高
2.5 枚举类方式
// 最安全
public enum EnumSingle {
INSTANCE;
public static EnumSingle getInstance(){
return INSTANCE;
}
}
优点:
1. 写法简单
2. 线程安全
3. 避免反序列化破坏单例