设计模式-创建型模式-单例模式-懒汉式
一、创建型模式
创造型模式主要的关注点是怎么创建对象,主要特点是将对象的创建和使用分离开来。以此降低系统的耦合度,使用者可以不需要过分关注对象的创建细节,直接使用就可以。
创造模式例子:
- 去商城购买商品,并不需要知道商品是怎么来的怎么生产的,只需要买来使用就可以;
- 去饭店吃饭,不需要考虑才是怎么做出来的,直接点了吃就完事;
二、单例模式
单例模式是创建型模式的其中一个模式,一般只一个类只有一个实例(只实例化一次),并且这个这个实例化的过程是在这个类本身完成的,只是对外开放了一个获取这个实例了的对象的方法。
单例模式例子:
- win系统内的回收站就是全局唯一的,不论在哪里删除或者创建图标,整个系统内只有一个回收站实例,创建的多个图标也都是指向了这个实例;
三、懒汉式
懒汉式单例是指类在用的时候才会实例化,并且只有在第一次被调用时会实例化一次,多次调用都是返回第一次实例化的对象。
1.单线程懒汉式单例
一下是简单地一个样例,该例子可以在单线程模式下保持全局单例:
代码段1:单线程懒汉模式单例
//代码段1:单线程懒汉模式单例
package com.lqh.singleton;
/**
* 单例模式-懒汉式
* @author lqh
* @version 1.0
* @date: 2021/7/9 21:23
*/
public class LazySingleton {
/**
* 在类内私有化一个静态对象,也就是唯一的实例,外面获取的实例都是指向这个对象
*/
private static LazySingleton instance;
/**
* 构造方法私有化,防止在其他地方被实例化
* 此处虽然私有化了构造方法,但是还是能被反射获取到并调用
*/
private LazySingleton(){}
/**
* 返回本类的实例对象,如果对象为空代表还没有被实例化,
* 就需要调用构造方法实例化后返回
* @return
*/
public static LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}
然后运行测试一下
代码段2:单线程测试懒汉模式单例
//代码段2:单线程测试懒汉模式单例
package com.lqh;
import com.lqh.singleton.LazySingleton;
import java.util.ArrayList;
import java.util.List;
/**
* 主类
* @date 22:24 2021/7/9
* @author lqh
*/
public class Main {
/**
* 比较两个对象是否相同
* @param i1
* @param i2
*/
private static void equals(LazySingleton i1, LazySingleton i2){
if (!i1.equals(i2)){
System.out.println("有不同的实例!");
}
}
public static void main(String[] args) {
//获取对象
LazySingleton instance1 = LazySingleton.getInstance();
//打印哈希码值
System.out.println(instance1.toString());//com.lqh.singleton.LazySingleton@1b6d3586
LazySingleton instance2 = LazySingleton.getInstance();
//打印哈希码值
System.out.println(instance2.toString());//com.lqh.singleton.LazySingleton@1b6d3586
//比较两个对象是否相同
System.out.println(instance1.equals(instance2));//true
//获取十次并加入列表
List<LazySingleton> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
//获取实例
LazySingleton instance = LazySingleton.getInstance();
//将获取的实例加入列表
list.add(instance);
}
//遍历列表打印每个实例的哈希码值
list.forEach(x -> System.out.println(x));//无一例额外全部都是com.lqh.singleton.LazySingleton@1b6d3586
//遍历列表将每个实例都与列表内的所有实例比对一遍,有不同的就输出不同
list.forEach(x -> {
for (LazySingleton instance : list) {
equals(x, instance);//全部相同
}
});
}
}
运行结果:
com.lqh.singleton.LazySingleton@1b6d3586
com.lqh.singleton.LazySingleton@1b6d3586
true
com.lqh.singleton.LazySingleton@1b6d3586
com.lqh.singleton.LazySingleton@1b6d3586
com.lqh.singleton.LazySingleton@1b6d3586
...
上面可以看到在main线程下生成的所有LazySingleton对象都指向了同一个哈希码值的对象,毕竟他们都是调用的同一个方法(getInstance):
//代码段1片段
public static LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
并且返回的也都是instance这个对象:
//代码段1片段
private static LazySingleton instance;
但是如果在多线程模式下这个方法就不在适用,因为多线程模式下可以看做是两个或多个线程同时在执行,比如两个线程A和B,线程A在调用getInstance()方法的时候检测到instance对象为空实例化了一个对象,线程B在A正在实例化对象的时候也调用了getInstance()方法,此时A线程还没有实例化完成,导致B线程也检测到instance对象时空,也会进行实例化,这就导致了两个对象的生成,打破了单例模式。
多线程测试:
代码段3:多线程测试懒汉模式单例
//代码段3:多线程测试懒汉模式单例
package com.lqh;
import com.lqh.singleton.LazySingleton;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 主类
* @date 22:24 2021/7/9
* @author lqh
*/
public class Main {
/**
* 比较两个对象是否相同
* @param i1
* @param i2
*/
private static void equals(LazySingleton i1, LazySingleton i2){
if (!i1.equals(i2)){
System.out.println("有不同的实例!");
}
}
public static void main(String[] args) {
//创建一个线程安全的列表,用来保存实例对象
List<LazySingleton> list = Collections.synchronizedList(new ArrayList<>());
//创建十个线程,且每个线程获取LazySingleton实例
for (int i = 0; i < 10; i++) {
new Thread(() ->{
LazySingleton instance = LazySingleton.getInstance();
list.add(instance);
}, "线程" + (i+1)).start();
}
//遍历列表打印每个实例的哈希码值
list.forEach(x -> System.out.println(x));
//遍历列表比较每一个实例是否相同
list.forEach(x -> {
for (LazySingleton instance : list) {
equals(x,instance);
}
});
}
}
运行结果:
com.lqh.singleton.LazySingleton@3d075dc0
com.lqh.singleton.LazySingleton@214c265e
com.lqh.singleton.LazySingleton@448139f0
com.lqh.singleton.LazySingleton@7cca494b
com.lqh.singleton.LazySingleton@7cca494b
com.lqh.singleton.LazySingleton@448139f0
com.lqh.singleton.LazySingleton@448139f0
com.lqh.singleton.LazySingleton@448139f0
com.lqh.singleton.LazySingleton@448139f0
com.lqh.singleton.LazySingleton@448139f0
有不同的实例!
有不同的实例!
有不同的实例!
有不同的实例!
有不同的实例!
...
可以看到在多线程模式下就产生了多个不同的对象,为了避免这个情况就需要用到锁了,改进一下代码,只改动了getInstance()方法:
2.多线程懒汉式单例
代码段4:懒汉模式单例加锁(多线程)
//代码段4:懒汉模式单例加锁
package com.lqh.singleton;
/**
* 单例模式-懒汉式
* @author lqh
* @version 1.0
* @date: 2021/7/9 21:23
*/
public class LazySingleton {
/**
* 在类内私有化一个静态对象,也就是唯一的实例,外面获取的实例都是指向这个对象
*/
private static LazySingleton instance;
/**
* 构造方法私有化,防止在其他地方被实例化
* 此处虽然私有化了构造方法,但是还是能被反射获取到并调用
*/
private LazySingleton(){}
/**
* 返回本类的实例对象,如果对象为空代表还没有被实例化,
* 就需要调用构造方法实例化后返回
* @return
*/
public static LazySingleton getInstance(){
//在检测时加一层锁,锁住本类
synchronized(LazySingleton.class){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}
}
再次运行单线程(代码段2)和多线程(代码段3)的代码:
单线程:
com.lqh.singleton.LazySingleton@1b6d3586
com.lqh.singleton.LazySingleton@1b6d3586
true
com.lqh.singleton.LazySingleton@1b6d3586
com.lqh.singleton.LazySingleton@1b6d3586
...
多线程:
com.lqh.singleton.LazySingleton@3d075dc0
com.lqh.singleton.LazySingleton@3d075dc0
com.lqh.singleton.LazySingleton@3d075dc0
...
可以看到在加了一次锁之后在多线程模式下就又成功回到单例模式
3.完整版懒汉模式单例
由于以下内容暂时还无法理解所以先只是贴出有待再研究
package com.lqh.singleton;
/**
* 单例模式-懒汉式
* @author lqh
* @version 1.0
* @date: 2021/7/9 21:23
*/
public class LazySingleton {
/**
* 在类内私有化一个静态对象,也就是唯一的实例,外面获取的实例都是指向这个对象
* 新增volatile关键字防止指令重排(?何为指令重排)
*/
private static volatile LazySingleton instance;
/**
* 构造方法私有化,防止在其他地方被实例化
* 此处虽然私有化了构造方法,但是还是能被反射获取到并调用
*/
private LazySingleton(){}
/**
* 返回本类的实例对象,如果对象为空代表还没有被实例化,
* 就需要调用构造方法实例化后返回
* @return
*/
public static LazySingleton getInstance() {
//第一层检查是否实例化对象
if (instance == null) {
//加锁避免线程不安全
synchronized (LazySingleton.class) {
//再次检查是否有实例化,没有就实例化(?为何二次检查)
if (instance == null) {
instance = new LazySingleton();
}
}
}
//返回
return instance;
}
}
运行单线程(代码片段2)和多线程(代码片段3)的测试结果和多线程懒汉式单例的结果一样就不再次贴出