目录
1.什么是单例模式
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。
2. 单例模式的类型
一:饿汉式单例
package com.shusl.single;
/*
饿汉式单例
很饿,一上来就把对象加载了!
问题:有可能浪费内存
我想用的时候再去创建这个对象,解决办法:懒汉式单例
单例中最重要的思想:构造器私有
一旦私有构造器了,别人就无法new这个对象了,保证了内存中只有一个对象
*/
public class Hungry {
//可能会浪费空间,开辟了空间,却没有使用
private Hungry(){
}
//饿汉式:先 new 对象, 可以保证唯一的
private final static Hungry HUNGRY = new Hungry();
//抛出一个对外的方法
public static Hungry getInstance(){
return HUNGRY;
}
}
A类调用:
二:懒汉式
package com.shusl.single;
/*
懒汉式单例
用的时候再去加载
*/
public class LazyMan01 {
private LazyMan01(){
System.out.println("LazyMan01");
};
private static LazyMan01 lazyMan01;
public static LazyMan01 getInstance(){
if (lazyMan01 == null) {
lazyMan01 = new LazyMan01();
}
return lazyMan01;
}
}
多线程并发测试,破坏单例
//多线程并发
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan01.getInstance();
}).start();
}
/*
*LazyMan01
LazyMan01
LazyMan01
*/
}
多线程并发破坏单例,解决加锁
解决指令重排 :volatile
DCL懒汉式
package com.shusl.single;
public class LazyMan02 {
private LazyMan02(){
};
private volatile static LazyMan02 lazyMan02;//volatile 避免指令重排
//双重检测锁模式的懒汉式单例 DCL
public static LazyMan02 getInstance(){
if(lazyMan02 == null){
synchronized (LazyMan02.class){//锁class只有一个
if (lazyMan02 == null) {
lazyMan02 = new LazyMan02();//有问题,不是原子性操作
/**
* 不是原子性操作,会经过三步操作 (底层操作)
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
* 这样才能保证这个对象 new 完成
*
* 会发生一个指令重排的情况:
* 比如:我们希望它执行123,但是它真实执行了132,这就完了
* 比如:第一个线程 A 进来时,执行了132(先把空间占用下来,再把内存对象放进去),
* 然后 A 线程没有问题,但是当B线程来的时候就有问题了
* 线程 B 由于它已经指向这个内存空间了,它会人为 LazyMan02 != null,会return LazyMan02,
* 这个时候 这个LazyMan02还没有完成构造,它的空间是虚无的,就会产生一个问题。
*
* 所有为了安全,一定要在 LazyMan02 中加 volatile,避免指令重排的
*/
}
}
}
return lazyMan02;
}
}
三:静态内部类
package com.shusl.single;
//静态内部类
public class Holder {
//构造器私有
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,因为有反射
反射破坏单例测试一:
package com.shusl.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyMan02 {
private LazyMan02(){
System.out.println("LazyMan01");
};
private volatile static LazyMan02 lazyMan02;//volatile 避免指令重排
//双重检测锁模式的懒汉式单例 DCL
public static LazyMan02 getInstance(){
if(lazyMan02 == null){
synchronized (LazyMan02.class){//锁class只有一个
if (lazyMan02 == null) {
lazyMan02 = new LazyMan02();
}
}
}
return lazyMan02;
}
public static void main(String[] args) throws Exception{
LazyMan02 lazyMan02 = LazyMan02.getInstance();
Constructor<LazyMan02> constructor = LazyMan02.class.getDeclaredConstructor(null); //获得构造器
constructor.setAccessible(true);//无视私有构造器
LazyMan02 instance = constructor.newInstance();//创建对象
System.out.println(instance);
System.out.println(lazyMan02);
/**
* LazyMan01
* LazyMan01
* com.shusl.single.LazyMan02@1b6d3586
* com.shusl.single.LazyMan02@4554617c
*/
}
}
解决反射加锁:
package com.shusl.single;
import java.lang.reflect.Constructor;
public class LazyMan03 {
private LazyMan03() {
synchronized (LazyMan03.class) {
if (lazyMan03 != null) {
throw new RuntimeException("不要试图反射破坏异常");
}
}
}
private volatile static LazyMan03 lazyMan03;//volatile 避免指令重排
//双重检测锁模式的懒汉式单例 DCL
public static LazyMan03 getInstance() {
if (lazyMan03 == null) {
synchronized (LazyMan03.class) {//锁class只有一个
if (lazyMan03 == null) {
lazyMan03 = new LazyMan03();
}
}
}
return lazyMan03;
}
public static void main(String[] args) throws Exception {
LazyMan03 lazyMan03 = LazyMan03.getInstance();
Constructor<LazyMan03> constructor = LazyMan03.class.getDeclaredConstructor(null); //获得构造器
constructor.setAccessible(true);//无视私有构造器
LazyMan03 instance = constructor.newInstance();//创建对象
System.out.println(instance);
System.out.println(lazyMan03);
}
}
问题:两个对象都是反射来定义,破坏单列?
public static void main(String[] args) throws Exception {
//LazyMan03 lazyMan03 = LazyMan03.getInstance();
Constructor<LazyMan03> constructor = LazyMan03.class.getDeclaredConstructor(null); //获得构造器
constructor.setAccessible(true);//无视私有构造器
LazyMan03 instance = constructor.newInstance();//创建对象
LazyMan03 instance2 = constructor.newInstance();//创建对象
System.out.println(instance);
System.out.println(instance2);
/**
* com.shusl.single.LazyMan03@2a33fae0
* com.shusl.single.LazyMan03@707f7052
*/
}
解决:红绿灯:定义标志位
package com.shusl.single;
import java.lang.reflect.Constructor;
public class LazyMan04 {
//定义标志位
private static boolean shu = false;
private LazyMan04() {
synchronized (LazyMan04.class) {
if (shu == false){
shu = true;
} else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan04 lazyMan04;//volatile 避免指令重排
//双重检测锁模式的懒汉式单例 DCL
public static LazyMan04 getInstance() {
if (lazyMan04 == null) {
synchronized (LazyMan03.class) {//锁class只有一个
if (lazyMan04 == null) {
lazyMan04 = new LazyMan04();
}
}
}
return lazyMan04;
}
public static void main(String[] args) throws Exception {
Constructor<LazyMan04> constructor = LazyMan04.class.getDeclaredConstructor(null); //获得构造器
constructor.setAccessible(true);//无视私有构造器
LazyMan04 instance = constructor.newInstance();//创建对象
LazyMan04 instance2 = constructor.newInstance();//创建对象
System.out.println(instance);
System.out.println(instance2);
}
}
反射获取字段来破坏单例:
public static void main(String[] args) throws Exception {
Field shu = LazyMan04.class.getDeclaredField("shu");
shu.setAccessible(true);//把这个私有权限也破坏
Constructor<LazyMan04> constructor = LazyMan04.class.getDeclaredConstructor(null); //获得构造器
constructor.setAccessible(true);//无视私有构造器
LazyMan04 instance = constructor.newInstance();//创建对象
shu.set(instance,false);
LazyMan04 instance2 = constructor.newInstance();//创建对象
System.out.println(instance);
System.out.println(instance2);
/**
* com.shusl.single.LazyMan04@4554617c
* com.shusl.single.LazyMan04@74a14482
*/
}
}
源码来分析:点入newInstance知道enum反射不能破坏
四:枚举
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
为什么枚举可以,探寻???
打开查看枚举构造方法测试下是不是无参构造:报错没有无参构造
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
//java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
System.out.println(instance);
System.out.println(instance2);
}
}
反编译这个类,
反编译软件编译之后
知道有参的测试
package com.shusl.single;
import java.lang.reflect.Constructor;
//enmu是什么?本身也是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
/*Cannot reflectively create enum objects*/
}
}