一文带你搞懂单例设计模式

更多知识,请移步我的小破站:http://hellofriend.top

什么是单例设计模式?

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。

通过单例模式可以保证系统中,应用该模式的类只有一个实例。即一个类只有一个对象实例。

在Java语言中,单例带来了两大好处:

  1. 对于频繁使用的对象(数据源、Session工厂),可以省略创建对象所花费的时间,这对于重量级的对象而言,是非常可观的一笔系统开销。
  2. 由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。

具体实现

需要:
(1)将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
(2)在该类内部产生一个唯一的实例化对象,并且将其封装为 private static 类型。
(3)定义一个静态方法返回这个唯一对象。

实现一:饿汉式 / 静态常量

  • 立即加载就是使用类的时候已经将对象创建完毕(不管以后会不会使用到该实例化对象,先创建了)。常见的实现办法就是直接 new 实例化。

代码如下:

public class HungrySingletonStaticInstance {
    public static void main(String[] args) {
        System.out.println("======HungrySingletonStaticInstance======");
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton {

    // 将自身实例化对象设置为一个属性,并用static、final修饰
    private final static Singleton instance = new Singleton();

    // 构造方法私有化
    private Singleton() {}

    // 提供静态方法返回该实例
    public static Singleton getInstance() {
        return instance;
    }
}

“饿汉模式” 的优缺点分析:

  • 优点:实现起来简单,没有多线程同步问题。
  • 缺点:当类 Singleton 被加载的时候,会初始化 staticinstance,静态变量被创建并分配内存空间,从这以后,这个 staticinstance对象便一直占着这段内存,可能造成内存浪费(即便你还没有用到这个实例)。当类被卸载时,静态变量被摧毁,并释放所占有内存,在某些特定条件下会耗费内存。
  • 补充:如果方法内有其他 static 方法,调用该方法此类也加载初始化。

实现二:饿汉式 / 静态代码块

public class HungrySingletonStaticBlock {
    public static void main(String[] args) {
        System.out.println("======HungrySingletonStaticBlock======");
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton {

    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}
  • 如果方法内有其他static方法,调用该方法此类也加载初始化。

实现三:懒汉式 / 线程不安全

  • 延迟加载就是调用 getInstance() 方法时实例才被创建(先不急着实例化出对象,等要用的时候才创建出来)。常见的实现方法就是在getInstance() 方法中进行 new 实例化。
public class LazyLoadingSingletonThreadUnSafe {
    public static void main(String[] args) {
        System.out.println("======LazySingletonThreadUnsafe======");
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton {

    // 将自身实例化对象设置为一个属性,并用static修饰
    private static Singleton instance;

    // 构造方法私有化
    private Singleton() {}

    // 静态方法返回该实例
    public static Singleton getInstance() {
        if (instance == null) {
            //TODO 线程在这里被阻塞,则此时对象没有被创建,UnSafe
            instance = new Singleton();
        }
        return instance;
    }
}

“懒汉模式” 的优缺点分析:

  • 优点:实现起来比较简单,当类 Singleton 被加载的时候,静态变量 staticinstance 未被创建并分配内存空间,当getInstance() 方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存。
  • 缺点:在多线程环境中,这种实完现方法是错误的,不能保证单例的状态。
  • 补充:如果方法内有其他static方法,调用该方法此类不会加载初始化。

实现四:懒汉式 / 线程安全 Sync

  • 静态方法返回该实例,加 Synchronized 关键字实现同步。
public class LazyLoadingSingletonSync {
    public static void main(String[] args) {
        System.out.println("======LazyLoadingSingletonSync======");
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton {

    // 将自身实例化对象设置为一个属性,并用static修饰
    private static Singleton instance;

    // 构造方法私有化
    private Singleton() {}

    // 静态方法返回该实例
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

线程安全的“懒汉模式”(加锁)的优缺点:

  • 优点:在多线程情形下,保证了“懒汉模式”的线程安全。
  • 缺点:在多线程情形下,synchronized方法通常效率低,显然这不是最佳的实现方案。
  • 补充:如果方法内有其他static方法,调用该方法此类不会加载初始化。

实现五:DCL双重检查锁定机制

(DCL:Double Checked Locking)

public class LazyLoadingSingletonDCL {
    public static void main(String[] args) {
        System.out.println("======LazyLoadingSingletonDCL======");
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton {

    // 将自身实例化对象设置为一个属性,并用 volatile、static 修饰
    private volatile static Singleton instance;

    // 构造方法私有化
    private Singleton() {}

    // 静态方法返回该实例
    public static Singleton getInstance() {
        // 第一次检查instance是否被实例化出来
        if (instance == null) {
            synchronized (Singleton.class) {
                // 某个线程取得了类锁,实例化对象前第二次检查 instance 是否已经被实例化
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 该方法是单例模式推荐的实现方式。内存占用率高,效率高,线程安全,多线程操作原子性。
  • 如果方法内有其他 static 方法,调用该方法此类不会加载初始化。

实现六:静态内部类

public class StaticInternalSingleton {
    public static void main(String[] args) {
        System.out.println("======StaticInternalSingleton======");
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}


class Singleton {

    private Singleton() {}

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        //加载 Singleton 类时并不会加载内部类
        private static final Singleton INSTANCE = new Singleton();
    }
}
  • 第一次加载 Singleton 类时并不会加载内部类初始化 Instance,只有第一次调用 getInstance 方法时虚拟机加载 SingletonHolder 并初始化 Instance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。

实现七:枚举单例

该方法借助 JDK 1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

public class EnumSingleton {
    public static void main(String[] args) {
        System.out.println("======EnumSingleton======");
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println("instance1 == instance2:" + (instance1 == instance2));//true

        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());

        instance1.method();
        instance2.method();
    }
}

enum Singleton {
    INSTANCE;

    public void method() {
        System.out.println("EnumSingleton");
    }
}

JDK 中单例模式的应用之 Runtime 类

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }
	//TODO 私有化构造器
    private Runtime() {}

很显然,JDK 中的 Runtime 类使用的是本文的 实现一:饿汉式 / 静态常量来实现的,由于 Runtime 类在程序运行中一定会使用到,所以并不会带来饿汉式的空间浪费问题。

小总结

1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统的性能。

2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
责任链设计模式是一种行为型设计模式,用于将请求的发送者和接收者解耦,使多个对象都有机会处理该请求。该模式将这些对象串成链,并沿着这条链传递请求,直到有一个对象能够处理它为止。 责任链模式的核心是定义一个处理请求的抽象类或接口,然后让多个具体的处理者对象继承或实现这个类/接口。每个具体的处理者对象都包含一个对下一个处理者对象的引用,形成一个链式结构。 当一个请求进入责任链时,责任链中的每个处理者都有机会处理该请求。如果可以处理请求,则进行处理;如果不能处理,则将请求传递给下一个处理者,直到有一个处理者能够处理它。 责任链模式的关键点是要找到合适的处理者顺序和条件。通常情况下,责任链模式适用于以下情况: 1. 有多个对象可以处理同一类型的请求,但具体由哪个对象来处理由运行时决定。 2. 不明确请求的接收者,希望请求在一个对象链中流动,直到被处理。 3. 需要动态地指定可以处理请求的对象集合。 使用责任链模式可以实现请求发送者和接收者的解耦,增加代码的灵活性和可扩展性。但同时也需要注意责任链的长度和效率问题,避免责任链过长或造成性能问题。 总结一下,责任链设计模式是一种将请求发送者和接收者解耦的设计模式,通过将多个处理者对象串成链,沿着这条链传递请求,直到有一个处理者能够处理它。这样可以增加代码的灵活性和可扩展性,适用于有多个对象可以处理同一类型请求的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值