Java单例模式的深入了解
单例模式是一种常用的设计模式,它确保一个类只有一个实例,同时提供一种全局访问的方式。
在Java中,单例模式有多种实现方式,我们既可以使用经典的饿汉式实现,也可以使用懒汉式、静态内部类等方式实现。本篇攻略将为大家深入讲解Java单例模式的各种实现方式及其优缺点,同时提供一些示例说明。
一、饿汉式单例模式
饿汉式单例模式是最简单的一种实现方式,它的核心思想是在类加载时创建实例对象,以确保全局只有一个实例。
饿汉式单例模式的实现方式如下:
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
在以上代码中,我们将实例对象instance定义成static final类型,确保了全局只有一个实例。同时,构造函数被声明为private,避免了外部直接创建实例。
饿汉式单例模式的优点:
- 实现简单,代码量少。
- 线程安全,可以保证全局只有一个实例。
饿汉式单例模式的缺点:
- 实例对象在类加载时被创建,如果该实例从未被使用,就会造成内存浪费。
二、懒汉式单例模式
懒汉式单例模式是另一种常用的实现方式,它的核心思想是在第一次使用时创建实例对象,以减少内存浪费。
懒汉式单例模式的实现方式如下:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在以上代码中,我们将实例对象instance初始化为null,只有在第一次调用getInstance()方法时才会创建实例对象。同时,getInstance()方法被声明为synchronized类型,确保线程安全。
懒汉式单例模式的优点:
- 只有在需要时才会创建实例,可以避免内存浪费。
- 线程安全,可以保证全局只有一个实例。
懒汉式单例模式的缺点:
- 每次调用getInstance()方法都需要进行线程同步,可能会影响性能。
- 由于是在第一次使用时才创建实例,因此如果多线程同时调用getInstance()方法,可能会创建多个实例。
三、静态内部类单例模式
静态内部类单例模式是一种较为优雅的实现方式,它的核心思想是在内部类中创建实例对象,以确保全局只有一个实例。
静态内部类单例模式的实现方式如下:
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
在以上代码中,我们将实例对象instance放在内部静态类SingletonHolder中,当getInstance()方法被调用时,才会初始化SingletonHolder类,并创建唯一的实例对象。
静态内部类单例模式的优点:
- 实现简单,线程安全。
- 只有在需要时才会创建实例,可以避免内存浪费。
静态内部类单例模式的缺点:
- 需要理解静态内部类的概念,阅读代码的过程中可能需要花费更多时间。
四、示例说明
1. 单线程环境下的示例
在单线程环境下,无论使用哪种实现方式,都可以保证全局只有一个实例:
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
assert(singleton1 == singleton2); // true
在以上代码中,我们通过两次调用getInstance()方法获取Singleton实例,然后通过assert()方法验证它们是否相等。
2. 多线程环境下的示例
在多线程环境下,饿汉式单例模式会产生多个实例的问题,懒汉式单例模式和静态内部类单例模式则可以通过加锁的方式保证线程安全:
// 懒汉式单例模式示例
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Singleton> results = new ArrayList<>();
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
Singleton singleton = Singleton.getInstance();
results.add(singleton);
});
}
executor.shutdown();
while (!executor.isTerminated());
assert(results.size() == 1);
// 静态内部类单例模式示例
ExecutorService executor = Executors.newFixedThreadPool(2);
List<Singleton> results = new ArrayList<>();
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
Singleton singleton = Singleton.getInstance();
results.add(singleton);
});
}
executor.shutdown();
while (!executor.isTerminated());
Set<Singleton> set = new HashSet<>(results);
assert(results.size() == 1);
assert(set.size() == 1);
在以上代码中,我们通过线程池执行100个线程,每个线程都会调用getInstance()方法获取Singleton实例,并将它们添加到List或Set中。
由于饿汉式单例模式没有考虑多线程的情况,因此results列表可能会包含多个实例。而懒汉式单例模式和静态内部类单例模式使用了加锁的方式,确保了全局只有一个实例。其中,静态内部类单例模式需要验证List和Set中是否只有一个实例,因为存在单例被反序列化后出现多个实例的情况。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java单例模式的深入了解 - Python技术站