单例模式
- 定义 保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
- 本质 控制实例数量
- 实例 我们在开发项目中经常用到读取配置文件 配置文件里面的配置多少公用的,整个项目都会使用,我们经常会把他做成一个工具类使用。如果不考虑单例模式的话实现如下:
SystemConfig
public class SystemConfig {
/**
* 类型
*/
private String type;
/**
* 个数
*/
private int num;
public SystemConfig() {
readConfig();
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
/**
* 读取配置文件
*/
private void readConfig() {
Properties properties = new Properties();
InputStream resourceAsStream = null;
try {
resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("system_config.properties");
properties.load(resourceAsStream);
String type = properties.getProperty("system.type");
String num = properties.getProperty("system.num");
this.type = type;
//默认配置文件值一定存在
this.num = Integer.parseInt(num);
} catch (IOException e) {
e.printStackTrace();
}
}
}
system_config.properties
system.type=APP
system.num=5
调用
@Test
public void testConfig(){
SystemConfig systemConfig = new SystemConfig();
System.out.println("配置类型:"+systemConfig.getType()+" 配置个数:"+systemConfig.getNum());
}
存在问题: 如上其实存在问题 使用是通过new 一个实例来使用 系统中如果多处使用就会new多个实例,这样会严重浪费内存资源。如果配置文件的内容少,问题还小一点,如果配置文件内容多,那么对系统资源的浪费就大了。
解决问题 引入单例 让整个应用中只出现一个SystemConfig 实例
饿汉式实现
public class SystemConfig {
/**
* 定义一个变量来存储创建好的实例(只创建一次)
*/
private static SystemConfig instance = new SystemConfig();
/**
* 私有构造方法
*/
private SystemConfig() {
readConfig();
}
public static SystemConfig getInstance() {
return instance;
}
/**
* 类型
*/
private String type;
/**
* 个数
*/
private int num;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
/**
* 读取配置文件
*/
private void readConfig() {
Properties properties = new Properties();
InputStream resourceAsStream = null;
try {
resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("system_config.properties");
properties.load(resourceAsStream);
String type = properties.getProperty("system.type");
String num = properties.getProperty("system.num");
this.type = type;
//默认配置文件值一定存在
this.num = Integer.parseInt(num);
} catch (IOException e) {
e.printStackTrace();
}
}
}
懒汉式 实现
public class SystemConfig {
/**
* 定义一个变量来存储创建好的实例(只创建一次)
*/
private static SystemConfig instance = null;
/**
* 私有构造方法
*/
private SystemConfig() {
readConfig();
}
/**
* 先判断没有则创建
* @return
*/
public static synchronized SystemConfig getInstance() {
if(instance == null){
instance = new SystemConfig();
}
return instance;
}
/**
* 类型
*/
private String type;
/**
* 个数
*/
private int num;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
/**
* 读取配置文件
*/
private void readConfig() {
Properties properties = new Properties();
InputStream resourceAsStream = null;
try {
resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("system_config.properties");
properties.load(resourceAsStream);
String type = properties.getProperty("system.type");
String num = properties.getProperty("system.num");
this.type = type;
//默认配置文件值一定存在
this.num = Integer.parseInt(num);
} catch (IOException e) {
e.printStackTrace();
}
}
}
调用
public void testHungry(){
SystemConfig systemConfig = SystemConfig.getInstance();
System.out.println("配置类型:"+systemConfig.getType()+" 配置个数:"+systemConfig.getNum());
}
其实懒汉式如果去掉 synchronized 会有并发问题
@Test
public void testConcurrent() throws InterruptedException {
System.out.println("100准备就绪.......");
long start = System.currentTimeMillis();
CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
countDownLatch.await();
//com.junli.hungry.SystemConfig instance = com.junli.hungry.SystemConfig.getInstance();
com.junli.lazy.SystemConfig instance = com.junli.lazy.SystemConfig.getInstance();
System.out.println("对象:" + instance);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
countDownLatch.countDown();
}
long end = System.currentTimeMillis();
System.out.println("100个线程已经执行完毕!总耗时:" + (end - start));
}
加上synchronized 又有性能问题!
一个即可避免懒汉式的资源浪费又可以避免饿汉式同步的时间浪费的方案
public class SystemConfigNew {
private static boolean initialized = false;
//默认使用SystemConfigNew的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
/**
* 私有构造方法
*/
private SystemConfigNew() {
synchronized (SystemConfigNew.class) {
if (initialized == false) {
initialized = !initialized;
} else {
throw new RuntimeException("单例已被侵犯");
}
}
readConfig();
}
/**
* 类型
*/
private String type;
/**
* 个数
*/
private int num;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
/**
* 读取配置文件
*/
private void readConfig() {
Properties properties = new Properties();
InputStream resourceAsStream = null;
try {
resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("system_config.properties");
properties.load(resourceAsStream);
String type = properties.getProperty("system.type");
String num = properties.getProperty("system.num");
this.type = type;
//默认配置文件值一定存在
this.num = Integer.parseInt(num);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @return
*/
public final static SystemConfigNew getInstance() {
return LazyHolder.LAZY;
}
/**
* 内部类
*/
public static class LazyHolder {
private final static SystemConfigNew LAZY = new SystemConfigNew();
}
}
懒汉式和饿汉式总结 懒汉式是以空间换时间,不管需不要都new出来,不用的时候会浪费资源。 饿汉式是以时间换空间,只有在需要的时候才去new,但是多了判断的时间。
缓存式单例 把对象存起来保证存起来的对象只有一个,每次用的时候从存的地方取。
SystemConfig
public class SystemConfig {
/**
* 唯一KEY
*/
private final static String CACHE_MAP_KEY = "one";
/**
* 保存实例的地方
*/
private static Map<String, SystemConfig> map = new ConcurrentHashMap<>();
/**
* 私有构造方法
*/
private SystemConfig() {
readConfig();
}
/**
* 先从map里面取 取不到就放一个
*
* @return
*/
public static synchronized SystemConfig getInstance() {
SystemConfig systemConfig = map.get(CACHE_MAP_KEY);
if (systemConfig == null) {
systemConfig = new SystemConfig();
map.put(CACHE_MAP_KEY, systemConfig);
}
return systemConfig;
}
/**
* 类型
*/
private String type;
/**
* 个数
*/
private int num;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
/**
* 读取配置文件
*/
private void readConfig() {
Properties properties = new Properties();
InputStream resourceAsStream = null;
try {
resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("system_config.properties");
properties.load(resourceAsStream);
String type = properties.getProperty("system.type");
String num = properties.getProperty("system.num");
this.type = type;
//默认配置文件值一定存在
this.num = Integer.parseInt(num);
} catch (IOException e) {
e.printStackTrace();
}
}
}
单例序列化问题
public class SystemConfig implements Serializable {
/**
* 定义一个变量来存储创建好的实例(只创建一次)
*/
private final static SystemConfig INSTANCE = new SystemConfig();
/**
* 私有构造方法
*/
public SystemConfig() {
readConfig();
}
public static SystemConfig getInstance() {
return INSTANCE;
}
/**
* 类型
*/
private String type;
/**
* 个数
*/
private int num;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
/**
* 读取配置文件
*/
private void readConfig() {
Properties properties = new Properties();
InputStream resourceAsStream = null;
try {
resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("system_config.properties");
properties.load(resourceAsStream);
String type = properties.getProperty("system.type");
String num = properties.getProperty("system.num");
this.type = type;
//默认配置文件值一定存在
this.num = Integer.parseInt(num);
} catch (IOException e) {
e.printStackTrace();
}
}
private Object readResolve() {
return INSTANCE;
}
}
问题测试
@Test
public void testSeriableConfig() {
com.junli.seriable.SystemConfig s1 = null;
com.junli.seriable.SystemConfig s2 = com.junli.seriable.SystemConfig.getInstance();
try {
FileOutputStream fileOutputStream = new FileOutputStream("SystemConfig.obj");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(s2);
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("SystemConfig.obj");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
s1 = (com.junli.seriable.SystemConfig) objectInputStream.readObject();
objectInputStream.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
如果没有readResolve() s1和s2不相等!
https://github.com/ljmomo/learn-pattern.git