创建型设计模式 - 单例模式的八种写法
一、单例模式
单例模式有以下8种写法:
- 饿汉式:
- 静态常量
- 静态代码块
- 懒汉式:
- 线程不安全
- 线程安全,同步方法
- 线程安全,同步代码块
- 双重检查
- 静态内部类
- 枚举
单例模式的使用场景:
需要频繁创建和销毁的对象;创建时耗时过多或消耗资源过多,但又经常用到的对象(比如session工厂、数据源等)
(1)饿汉式 - 静态常量写法
代码实现:
/**
* 设计模式之单例模式
* 饿汉式(静态常量)
*/
public class SingletonTest01 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton {
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton() {
}
//定义为常量,保证实例对象不变
private final static Singleton instance = new Singleton();
//通过此方法获取实例
public static Singleton getInstance() {
return instance;
}
}
分析:
优点:
- 使用方式简单,在类加载的时候创建实例对象,避免了线程同步问题
缺点:
- 在类加载的时候创建实例对象,但不确定何时使用、是否使用,可能造成内存浪费
(2)饿汉式 - 静态代码块写法
代码实现:
/**
* 设计模式之单例模式
* 饿汉式(静态代码块写法)
*/
public class SingletonTest02 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton{
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton(){
}
//定义为常量,保证实例对象不变
private final static Singleton instance;
static {
instance = new Singleton();
}
//通过此方法获取实例
public static Singleton getInstance(){
return instance;
}
}
分析:
和静态常量一致,只不过初始化的位置不同,一个在静态代码块,一个直接在常量声明处初始化
(3)懒汉式 - 线程不安全
代码实现:
/**
* 设计模式之单例模式
* 懒汉式(线程不安全)
*/
public class SingletonTest03 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton {
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton() {
}
//声明实例对象
private static Singleton instance;
//通过此方法获取实例
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
分析:
优点:
- 满足随用随拿的特点,解决了内存浪费的问题
缺点:
- 线程不安全,当多个线程访问时,可能创建多个实例,因此实际开发中不可使用
(4)懒汉式 - 线程安全 - 同步方法写法
代码实现:
/**
* 设计模式之单例模式
* 懒汉式(同步方法)
*/
public class SingletonTest04 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton {
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton() {
}
//声明实例对象
private static Singleton instance;
//通过此方法获取实例
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
分析:
虽然解决了线程不安全问题,但锁的范围太大,效率低,开发中尽量不要使用
(5)懒汉式 - 线程安全 - 同步代码块写法
代码实现:
/**
* 设计模式之单例模式
* 懒汉式(同步代码块写法)
*/
public class SingletonTest05 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton {
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton() {
}
//声明实例对象
private static Singleton instance;
//通过此方法获取实例
public static Singleton getInstance() {
if (instance == null) {
//同步代码
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
分析:
这种方式将同步锁缩小了范围,本意是解决效率问题,但又造成了线程不安全,因此开发中不可使用
(6)懒汉式 - 双重检查(推荐使用)
代码实现:
/**
* 设计模式之单例模式
* 双重检查
*/
public class SingletonTest06 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton {
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton() {
}
//声明实例对象
private static volatile Singleton instance;
//双重判断 + 同步锁
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
分析:
既提高了效率也解决了线程安全问题,推荐使用这种方法
(7)懒汉式 - 静态内部类(推荐使用)
代码实现:
/**
* 设计模式之单例模式
* 静态内部类
*/
public class SingletonTest07 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
class Singleton {
//私有构造方法,使其不可在外部通过构造器实例化
private Singleton() {
}
//静态内部类
private static class SingletonInstance{
private final static Singleton INSTANCE = new Singleton();
}
//获取实例
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
分析:
利用了类加载机制,保证初始化实例时只有一个线程。Singleton类被装载时并不会被实例化,当调用getInstance方法时才会装载SingletonInstance
(8)懒汉式 - 枚举法(推荐使用)
代码实现:
/**
* 设计模式之单例模式
* 枚举
*/
public class SingletonTest08 {
public static void main(String[] args) {
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
}
}
enum Singleton{
INSTANCE;
}
分析:
不仅能规避线程不安全,还能防止反序列化重新创建新的对象
p.s. 所有代码和笔记均可在 我的GitHub 中获取,如果对您有帮助的话,可以点个 star 支持一下 🙈