单例模式
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
优点
- 减少内存开支
- 减少了系统开销
- 避免资源占用(例如读写文件)
使用
例如我们经常使用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();
}
}