分享程序网
首页
  • java
微服务
微前端
环境搭建
数据库
设计模式
算法
软件
解决问题
链接
首页
  • java
微服务
微前端
环境搭建
数据库
设计模式
算法
软件
解决问题
链接
  • 创建模式

    • 单例模式
    • 工厂方法模式
    • 抽象工厂模式
    • 原型模式
    • 建造者模式
  • 结构模式

    • 适配器模式
    • 桥接模式
    • 组合模式
    • 装饰器模式
    • 门面模式
    • 享元模式
    • 代理模式
  • 行为模式

    • 责任链模式
    • 命令模式
    • 迭代器模式
    • 策略模式
    • 模板方法模式

单例模式

定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

优点

  1. 减少内存开支
  2. 减少了系统开销
  3. 避免资源占用(例如读写文件)

使用

​ 例如我们经常使用ModelMappper或者MapStruct进行属性拷贝,但是我们通常会先对其调用方法进行实例化,但是只实例化一次,下面举例说明。

饿汉式

package com.shareprog.manager.utils;
import org.modelmapper.ModelMapper;
public class ModelMapperSingle {
	protected static final ModelMapper MODEL_MAPPER = new ModelMapper();
	public static ModelMapper instance() {
		return ModelMapperSingle.MODEL_MAPPER;
	}
}

或者在静态代码块中实例化

package com.shareprog.manager.utils;

import org.modelmapper.ModelMapper;

public class ModelMapperSingle {
	protected static final ModelMapper MODEL_MAPPER;
	static {
		MODEL_MAPPER = new ModelMapper()
	}
	public static ModelMapper instance() {
		return ModelMapperSingle.MODEL_MAPPER;
	}
}

这种方式线程安全,执行效率高,但是就是不管用不用,该对象都被实例化,浪费了内存。

懒汉式

package com.shareprog.manager.utils;

import org.modelmapper.ModelMapper;

public class ModelMapperSingle {
	private ModelMapperSingle () {
		throw new IllegalStateException("ModelMapperSingle class");
	}
	protected static final ModelMapper MODEL_MAPPER = null;
	public static ModelMapper instance() {
		if (MODEL_MAPPER == null) {
			MODEL_MAPPER = new ModelMapper();
		}
		return ModelMapperSingle.MODEL_MAPPER;
	}
}

这种方式确实是不使用的时候不至于浪费内存,但是却是线程不安全的。所以需要加锁:

package com.shareprog.manager.utils;

import org.modelmapper.ModelMapper;

public class ModelMapperSingle {
	private ModelMapperSingle () {
		throw new IllegalStateException("ModelMapperSingle class");
	}
	protected static final ModelMapper MODEL_MAPPER = null;
	public synchronized static ModelMapper instance() {
		if (MODEL_MAPPER == null) {
			MODEL_MAPPER = new ModelMapper();
		}
		return ModelMapperSingle.MODEL_MAPPER;
	}
}

虽然通过加synchronized关键字实现了线程同步,但是在线程数量剧增的时候,又会导致大批线程阻塞,所以可以加双重检查锁

双重检查锁

package com.shareprog.manager.utils;

import org.modelmapper.ModelMapper;

public class ModelMapperSingle {
	private ModelMapperSingle () {
		throw new IllegalStateException("ModelMapperSingle class");
	}
	protected static final ModelMapper MODEL_MAPPER = null;
	public static ModelMapper instance() {
		// 检查是否要阻塞
		if (MODEL_MAPPER == null) {
			synchronized (ModelMapperSingle.class) {
                // 检查是否要重新创建实例
				if (MODEL_MAPPER == null) {
					MODEL_MAPPER = new ModelMapper();
                    // 指令重排序的问题
				}
			}
		}
		return ModelMapperSingle.MODEL_MAPPER;
	}
}

​ 当第一个线程调用instance()方法时,第二个线程也可以调用。当第一个线程执行到synchronized时会上锁,第二个线程会变成MONITOR状态,出现阻塞。因为不是对类的阻塞,所以逻辑不复杂的情况下几乎感觉不到。

​ 虽然双重检查锁解决了线程和性能问题,但是只要加上synchronized就总会上锁,对性能还是会有一定的影响。

静态内部类单例模式

package com.shareprog.manager.utils;

import org.modelmapper.ModelMapper;

public class ModelMapperSingle {
    //使用的时候,会默认先初始化内部类
    //如果没使用,内部类是不会加载的
	private ModelMapperSingle () {
		throw new IllegalStateException("ModelMapperSingle class");
	}
    //使用static是为了防止重写或重载
	public static ModelMapper instance() {
		return ModelMapperHolder.MODEL_MAPPER;
	}
    //利用JAVA的语法特征,默认不添加内部类
    private static class ModelMapperHolder {
        private static final ModelMapper MODEL_MAPPER = new ModelMapper();
    }
}

可是如果一次时正常调用,还有一次通过反射调用,就会出现两个。当然反射调用肯定是先去除了异常抛出的代码。

package com.shareprog.manager.utils;

import org.modelmapper.ModelMapper;

public class ModelMapperSingle {
    //使用的时候,会默认先初始化内部类
    //如果没使用,内部类是不会加载的
	private ModelMapperSingle () {
        if (ModelMapperHolder.MODEL_MAPPER != null) {
			throw new IllegalStateException("ModelMapperSingle class");
        }
	}
    //使用static是为了防止重写或重载
	public static ModelMapper instance() {
		return ModelMapperHolder.MODEL_MAPPER;
	}
    //利用JAVA的语法特征,默认不添加内部类
    private static class ModelMapperHolder {
        private static final ModelMapper MODEL_MAPPER = new ModelMapper();
    }
}

枚举式单例模式

Last Updated:
Contributors: clcheng
Next
工厂方法模式