java单例模式--懒汉式、饿汉式(第二次学习)

news2025/1/16 9:12:41

目录

java单例模式--懒汉式、饿汉式

1、要点

2、单例模式的类型(五种)

2.1 饿汉式

2.2 枚举饿汉式(推荐使用)

2.3 懒汉式单例

2.4 DCL懒汉式(双检锁的懒汉式)

2.5 内部类懒汉式(推荐使用)

3、jdk中的体现

1、饿汉式的单例模式

2、枚举饿汉式

3、懒汉式的DCL(双检锁)

4、内部类懒汉式


java单例模式--懒汉式、饿汉式

单例模式,属于创建类型的一种常用的软件设计模式。(21种设计模式之一)通过单例模式的方法创建的类在当前进程中只有一个实例

1、要点

  • 构造器私有化

  • 定义静态变量存储这个对象唯一实例

  • 直接暴露或者封装使用getter方法提供实例的获取

2、单例模式的类型(五种)

饿汉式、枚举饿汉式、懒汉式、双锁懒汉式、内部类懒汉式 五种

2.1 饿汉式

饿汉式:即当类初始化的时候就创建实例对象 --就叫做饿汉式

代码

package com.sofwin.singletonDemo;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-饿汉式
 *
 * 要点:
 *      1.构造器私有化
 *      2.定义静态常量存储对象唯一实例
 *      3.通过getter方法获取实例对象
 */
public class Singleton01 {
​
    /**
     * 构造器私有化
     */
    private static final Singleton01 SINGLETON_01 = new Singleton01();
​
​
    private  Singleton01() {
        System.out.println("private Singleton01");
    }
​
    public static Singleton01 getSingleton01() {
        return SINGLETON_01;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
}

测试

package com.sofwin.singletonDemo;
​
import org.junit.Test;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:09
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式的测试
 */
public class SingletonTest {
​
    //单例模式-饿汉式
    @Test
    public void SinletonTest01() {
        //先判断是否类初始化的时候就创建对象了
        Singleton01.otherMethod();
        System.out.println("=================");
        Singleton01 singleton01 = Singleton01.getSingleton01();
        Singleton01 singleton02 = Singleton01.getSingleton01();
        System.out.println(singleton01);
        System.out.println(singleton02);
    }
}

(类加载就创建实例---饿汉式) 

这种设计有三种方法可以进行破坏

1、反射破坏单例

package com.sofwin.singletonDemo;
​
import org.junit.Test;
​
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:09
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式的测试
 */
public class SingletonTest {
​
    //单例模式-饿汉式
    @Test
    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //先判断是否类初始化的时候就创建对象了
        Singleton01.otherMethod();
        System.out.println("=================");
        Singleton01 singleton01 = Singleton01.getSingleton01();
        Singleton01 singleton02 = Singleton01.getSingleton01();
        System.out.println(singleton01);
        System.out.println(singleton02);
        System.out.println("暴力破解");
        SingletonTest.reflection(Singleton01.class);
    }
​
    /**
     * 反射破坏单例pos
     * @param clazz  类对象
     */
    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取无参构造方法
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        //暴力破解
        declaredConstructor.setAccessible(true);
        //通过反射创建实例对象
        Object o = declaredConstructor.newInstance();
        System.out.println(o);
    }
}
​
通过反射暴力破解,得到新的实例对象 

改进

package com.sofwin.singletonDemo;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-饿汉式
 *
 * 要点:
 *      1.构造器私有化
 *      2.定义静态常量存储对象唯一实例
 *      3.通过getter方法获取实例对象
 */
public class Singleton01 {
​
    /**
     * 构造器私有化
     */
    private static final Singleton01 SINGLETON_01 = new Singleton01();
​
​
    private  Singleton01() {
        //防止反射破坏单例
        if (SINGLETON_01 !=null) {
          throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton01");
    }
​
    public static Singleton01 getSingleton01() {
        return SINGLETON_01;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
}
 

2、反序列化破坏单例

Singleton01类要实现Serializable

package com.sofwin.singletonDemo;
​
import java.io.Serializable;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-饿汉式
 *
 * 要点:
 *      1.构造器私有化
 *      2.定义静态常量存储对象唯一实例
 *      3.通过getter方法获取实例对象
 */
public class Singleton01 implements Serializable {
​
    /**
     * 构造器私有化
     */
    private static final Singleton01 SINGLETON_01 = new Singleton01();
​
​
    private  Singleton01() {
        //防止反射破坏单例
        if (SINGLETON_01 !=null) {
          throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton01");
    }
​
    public static Singleton01 getSingleton01() {
        return SINGLETON_01;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
}

测试代码

package com.sofwin.singletonDemo;
​
import org.junit.Test;
​
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:09
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式的测试
 */
public class SingletonTest {
​
    //单例模式-饿汉式
    @Test
    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        //先判断是否类初始化的时候就创建对象了
        Singleton01.otherMethod();
        System.out.println("=================");
        Singleton01 singleton01 = Singleton01.getSingleton01();
        Singleton01 singleton02 = Singleton01.getSingleton01();
        System.out.println(singleton01);
        System.out.println(singleton02);
//        System.out.println("反射暴力破解");
//        SingletonTest.reflection(Singleton01.class);
        System.out.println("反序列化破解");
        SingletonTest.serialzable(singleton02);
    }
​
    /**
     * 反射破坏单例pos
     * @param clazz  类对象
     */
    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取无参构造方法
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        //暴力破解
        declaredConstructor.setAccessible(true);
        //通过反射创建实例对象
        Object o = declaredConstructor.newInstance();
        System.out.println(o);
    }
​
    /**
     * 反序列破解单例模式
     * @param instance 对象
     */
    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
        //将对象读取到baos中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(instance);
        //通过字节数组获取对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        Object o = ois.readObject();
        System.out.println(o);
    }
}

 

改进

package com.sofwin.singletonDemo;
​
import java.io.Serializable;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-饿汉式
 *
 * 要点:
 *      1.构造器私有化
 *      2.定义静态常量存储对象唯一实例
 *      3.通过getter方法获取实例对象
 */
public class Singleton01 implements Serializable {
​
    /**
     * 构造器私有化
     */
    private static final Singleton01 SINGLETON_01 = new Singleton01();
​
​
    private  Singleton01() {
        //防止反射破坏单例
        if (SINGLETON_01 !=null) {
          throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton01");
    }
​
    public static Singleton01 getSingleton01() {
        return SINGLETON_01;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
​
    /**
     * 解决反序列化
     * @return  实例对象
     * 
     */
    public Object readResolve() {
        return SINGLETON_01;
    }
​
​
}
​ 

这里 readResolve() 方法的解释

简而言之,当我们通过反序列化readObejct方法获取对象的时候,会去寻找readResolve()方法,如果该方法不存在则直接返回新对象,如果该方法存在则按照该方法的内容返回对象,以确保如果我们之前实例化了单例对象,就返回该对象。

为什么要使用功能readResolve()方法就解决了?

  

这里看一下重点代码,readOrdinaryObject方法的代码片段:

 

isInstantiable:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。针对serializable和externalizable我会在其他文章中介绍。

desc.newInstance:该方法通过反射的方式调用无参构造方法新建一个对象。

然后关键是下面的代码

if (obj != null &&
    handles.lookupException(passHandle) == null &&
    desc.hasReadResolveMethod())
{
    Object rep = desc.invokeReadResolve(obj);
    if (unshared && rep.getClass().isArray()) {
        rep = cloneArray(rep);
    }
    if (rep != obj) {
        // Filter the replacement object
        if (rep != null) {
            if (rep.getClass().isArray()) {
                filterCheck(rep.getClass(), Array.getLength(rep));
            } else {
                filterCheck(rep.getClass(), -1);
            }
        }
        handles.setObject(passHandle, obj = rep);
    }
}

hasReadResolveMethod:如果实现了serializable 或者 externalizable接口的类中包含readResolve则返回true

invokeReadResolve:通过反射的方式调用要被反序列化的类的readResolve方法。

所以,原理也就清楚了,主要在Singleton中定义readResolve方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。

3、Unsafe破坏单例

package com.sofwin.singletonDemo;
​
import org.junit.Test;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
​
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:09
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式的测试
 */
public class SingletonTest {
​
    //单例模式-饿汉式
    @Test
    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        //先判断是否类初始化的时候就创建对象了
        Singleton01.otherMethod();
        System.out.println("=================");
        Singleton01 singleton01 = Singleton01.getSingleton01();
        Singleton01 singleton02 = Singleton01.getSingleton01();
        System.out.println(singleton01);
        System.out.println(singleton02);
//        System.out.println("反射暴力破解");
//        SingletonTest.reflection(Singleton01.class);
//        System.out.println("反序列化破解");
//        SingletonTest.serialzable(singleton02);
        System.out.println("unsafe");
        unsafe(singleton01.getClass());
    }
​
    /**
     * 反射破坏单例pos
     * @param clazz  类对象
     */
    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取无参构造方法
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        //暴力破解
        declaredConstructor.setAccessible(true);
        //通过反射创建实例对象
        Object o = declaredConstructor.newInstance();
        System.out.println(o);
    }
​
    /**
     * 反序列破解单例模式
     * @param instance 对象
     */
    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
        //将对象读取到baos中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(instance);
        //通过字节数组获取对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        Object o = ois.readObject();
        System.out.println(o);
    }
​
    /**
     * 使用spring的工具类在jdk底层来访问,无序调用构造方法
     * @param clazz
     * @throws InstantiationException
     */
    public static void unsafe(Class clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println(o);
    }
}

 

这个是无法解决的

2.2 枚举饿汉式(推荐使用)

代码

package com.sofwin.singletonDemo;
​
import sun.dc.pr.PRError;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 11:34
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式 --枚举饿汉式
 */
public enum Singleton02 {
     INSTANCE;
​
     private Singleton02() {
         System.out.println("private Singleton02");
     }
​
     public static Singleton02 getInstance() {
         return  INSTANCE;
     }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
​
    @Override
    public String toString() {
        //Integer.toHexString 转换为16进制
        return getClass().getName()+"@"+Integer.toHexString(hashCode());
    }
}

测试

package com.sofwin.singletonDemo;
​
import org.junit.Test;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
​
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:09
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式的测试
 */
public class SingletonTest {
​
    //单例模式-饿汉式
    @Test
    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        //先判断是否类初始化的时候就创建对象了
        Singleton01.otherMethod();
        System.out.println("=================");
        Singleton02 singleton01 = Singleton02.INSTANCE;
        Singleton02 singleton02 = Singleton02.INSTANCE;
        System.out.println(singleton01);
        System.out.println(singleton02);
        //1、反射不存在,枚举没有无参构造器
//        System.out.println("反射暴力破解");
//        SingletonTest.reflection(Singleton02.class);
        //2、反序列化不存在,readObject的时候会进行判断 ,如果是枚举类有特殊处理
//        System.out.println("反序列化破解");
//        SingletonTest.serialzable(singleton02);
        //3、jdk这个是无法避免的,是可以破坏的
//        System.out.println("unsafe");
//        unsafe(singleton01.getClass());
    }
​
    /**
     * 反射破坏单例pos
     * @param clazz  类对象
     */
    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取无参构造方法
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        //暴力破解
        declaredConstructor.setAccessible(true);
        //通过反射创建实例对象
        Object o = declaredConstructor.newInstance();
        System.out.println(o);
    }
​
    /**
     * 反序列破解单例模式
     * @param instance 对象
     */
    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
        //将对象读取到baos中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(instance);
        //通过字节数组获取对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        Object o = ois.readObject();
        System.out.println(o);
    }
​
    /**
     * 使用spring的工具类在jdk底层来访问,无序调用构造方法
     * @param clazz
     * @throws InstantiationException
     */
    public static void unsafe(Class clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println(o);
    }
}

1、反射无法破坏枚举类单例

枚举没有无参构造

 

修改成有参构造

/**
 * 反射破坏单例pos
 * @param clazz  类对象
 */
public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //获取无参构造方法
    Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);
    //暴力破解
    declaredConstructor.setAccessible(true);
    //通过反射创建实例对象
    Object o = declaredConstructor.newInstance("OTHER",1);
    System.out.println(o);
}

 

2、反序列话也无法破坏枚举类的单例

 3、Unsafe是可以破坏的

2.3 懒汉式单例

代码

package com.sofwin.singletonDemo;
​
import java.io.Serializable;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-懒汉式
 *
 * 要点:
 *      1.构造器私有化
 *      2.定义静态常量存储对象唯一实例
 *      3.通过getter方法获取实例对象
 */
public class Singleton03 implements Serializable {
​
    /**
     * 构造器私有化
     */
    private static  Singleton03 instance = null;
​
​
    private Singleton03() {
        //防止反射破坏单例
        if (instance !=null) {
          throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton01");
    }
​
    public static Singleton03 getSingleton01() {
        if (instance == null) {
            instance = new Singleton03();
        }
        return instance;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
​
    /**
     * 解决反序列化
     * @return  实例对象
     *
     */
    public Object readResolve() {
        return instance;
    }
​
​
}

测试

package com.sofwin.singletonDemo;
​
import org.junit.Test;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
​
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:09
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式的测试
 */
public class SingletonTest {
​
    //单例模式-饿汉式
    @Test
    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        //先判断是否类初始化的时候就创建对象了
        Singleton03.otherMethod();
        System.out.println("=================");
        Singleton03 singleton01 = Singleton03.getSingleton01();
        Singleton03 singleton02 = Singleton03.getSingleton01();
        System.out.println(singleton01);
        System.out.println(singleton02);
        //1、反射可以破坏单例
//        System.out.println("反射暴力破解");
//        SingletonTest.reflection(Singleton03.class);
        //2、反序列化也可以破坏
//        System.out.println("反序列化破解");
//        SingletonTest.serialzable(singleton02);
        //3、jdk这个是无法避免的,是可以破坏的
//        System.out.println("unsafe");
//        unsafe(singleton01.getClass());
    }
​
    /**
     * 反射破坏单例pos
     * @param clazz  类对象
     */
    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取无参构造方法
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        //暴力破解
        declaredConstructor.setAccessible(true);
        //通过反射创建实例对象
        Object o = declaredConstructor.newInstance();
        System.out.println(o);
    }
​
    /**
     * 反序列破解单例模式
     * @param instance 对象
     */
    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
        //将对象读取到baos中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(instance);
        //通过字节数组获取对象
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        Object o = ois.readObject();
        System.out.println(o);
    }
​
    /**
     * 使用spring的工具类在jdk底层来访问,无序调用构造方法
     * @param clazz
     * @throws InstantiationException
     */
    public static void unsafe(Class clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println(o);
    }
}

反射、序列化和unsafe都可以破坏

但是这样都会导致一个问题多线程的情况会出现错误情况 (之前,懒汉式的常量和静态代码块中的代码,其实是jvm底层维护的是线程安全的,无序考虑) 因为这个是方法中创建实例就会出现线程安全的问题

测试代码

public class SingletonTest {
​
    public static void main(String[]args){
       new Thread( new Runnable() {
            @Override
            public void run() {
                Singleton03 singleton01 = Singleton03.getSingleton01();
                System.out.println(singleton01);
            }
        }).start();
​
        new Thread( new Runnable() {
            @Override
            public void run() {
                Singleton03 singleton01 = Singleton03.getSingleton01();
                System.out.println(singleton01);
            }
        }).start();
    }
  }

正常应该是这种情况:

 但是多运行几次,也会出现这样的情况:

 造成的这种情况的结果是问题是:

 

这个时候就出现了DCL懒汉式(双检锁的懒汉式)

2.4 DCL懒汉式(双检锁的懒汉式)

package com.sofwin.singletonDemo;
​
import java.io.Serializable;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-懒汉式
 *
 * 要点:
 *      1.构造器私有化
 *      2.定义静态常量存储对象唯一实例
 *      3.通过getter方法获取实例对象
 */
public class Singleton04 implements Serializable {
​
    /**
     * 构造器私有化
     */
    //这里加入valatile是保证这个变量是有序的,可见的
    //防止指令重排序,在赋值语句后加入一个内存屏障,防止之前的赋值操作,进行重新排序
    private static volatile Singleton04 instance = null;
​
​
​
    private Singleton04() {
        //防止反射破坏单例
        if (instance !=null) {
          throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton01");
    }
​
    public static Singleton04 getSingleton01() {
        
        if (instance == null) {
            synchronized (Singleton04.class){
                if (instance == null) {
                    instance = new Singleton04();
                }
            }
        }
        return instance;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
​
    /**
     * 解决反序列化
     * @return  实例对象
     *
     */
    public Object readResolve() {
        return instance;
    }
​
​
}

这个代码可以这样理解:

public static Singleton04 getSingleton01() {

     if (instance == null) {

synchronized (Singleton04.class){

        if (instance == null) {

                  instance = new Singleton04();

          }

}

//里面这一块是进行加锁放在出现上面的情况

//外面的if判断是进行代码的优化

//我们这个线程锁只是还没创建对象实例的情况出现,因此加上if后

//我们就保证了后序的调用无需进行加锁操作,加快了效率

     }

return instance;

}

2.5 内部类懒汉式(推荐使用)

package com.sofwin.singletonDemo;
​
import java.io.Serializable;
​
/**
 * @packageName: com.sofwin.singletonDemo
 * @author: wentao
 * @date: 2022/11/21 10:00
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 单例模式-内部类懒汉式
 *
 * 要点:
 *      内部类不会随着玩不类的加载而初始化
 *      它是单独进行初始化和加载的,并且静态成员的赋值,都是在静态代码块中的,是底层保证线程安全的
 *      只有调用内部类的类变量的时候,才进行初始化,实现懒汉式模式
 */
public class Singleton05 implements Serializable {
​
    /**
     * 构造器私有化
     */
    private Singleton05() {
        System.out.println("private Singleton01");
    }
​
    private static class Holder {
        static  Singleton05 instance = new Singleton05();
    }
​
    public static Singleton05 getSingleton01() {
        return Holder.instance;
    }
​
    /**
     * 通过调用静态方法,实现类的初始化
     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
     */
    public static void otherMethod() {
        System.out.println("判断是懒汉式还是饿汉式");
    }
​
​
​
​
}

3、jdk中的体现

1、饿汉式的单例模式

System的gc() 垃圾回收机制中的Runtime.getRuntime().gc()

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
​
    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }
​
    /** Don't let anyone else instantiate this class */
    private Runtime() {}

2、枚举饿汉式

Comparator的naturalOrder()

 

3、懒汉式的DCL(双检锁)

System中的cons成员变量

 

4、内部类懒汉式

Collections中的emptyListInterator() 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue2 sass 安装及使用2

根据上一篇 vue2 sass 安装及使用_lsswear的博客-CSDN博客&#xff0c;使用vue-cli 5版本继续尝试安装sass。 本地环境&#xff1a; win10 vue2 vue-cli 5 sass安装 vue create testsass npm i node-loaderadded 2 packages in 6snpm i sass-loaderadded 1 package in 3s…

【pytorch】MNIST 梯度上升法求使得某类概率最大的样本

目标&#xff1a;用 MNIST 训练一个 CNN 模型&#xff0c;然后用梯度上升法生成一张图片&#xff0c;使得模型对这张图片的预测结果为 8 import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim import torchvi…

jsp科研管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 科研管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为Mysql&#xff0c;使用ja…

面试:KOOM内存泄漏的监控

LeakCannary 为什么各大厂自研的内存泄漏检测框架都要参考 LeakCanary&#xff1f;因为它是真强啊&#xff01;_慕课手记 内存快照是在触发了onDestory中做的 目前&#xff0c;LeakCanary 支持以下五种 Android 场景中的内存泄漏监测&#xff1a; 1、已销毁的 Activity 对象…

基于java+ssm的在线投票管理系统-计算机毕业设计

项目介绍 基于SSM的在线投票系统以XXX学院为背景&#xff0c;运用在校所学习的软件开发原理&#xff0c;采用SpringSpringMVCMyBatis技术和MySQL数据库构建一个基于B/S模式的在线投票系统。本系统在设计之初&#xff0c;结合网络上。现有的在线投票系统。经过具体分析之后都出…

【Go】 力扣 - 剑指 Offer 第五天 - 二维数组中的查找

[Go] 力扣 - 剑指 Offer 第五天 - 二维数组中的查找题目来源题目描述示例题目分析算法暴力法代码实现复杂度分析二分法代码实现复杂度分析模拟 BST 标记查找法代码实现复杂度分析结尾耐心和持久胜过激烈和狂热。 题目来源 来源&#xff1a;力扣&#xff08;LeetCode&#xff0…

TestStand-从LabVIEW创建TestStand数据类型的簇

文章目录从LabVIEW创建TestStand数据类型的簇从LabVIEW创建TestStand数据类型的簇 TestStand提供数字、字符串、布尔值和对象引用内置数据类型。 TestStand还提供了几种标准的命名数据类型&#xff0c;包括路径、错误、LabVIEW模拟波形等。可以通过创建容器数据类型来保存任何…

【第四部分 | JavaScript 基础】1:JS概述、变量及输入输出

目录 | 概述 | JS的书写位置 | 输入输出 | 变量 命名规范 基本使用 通过输入语句prompt把信息赋值给变量 | 数据类型 JS数据类型的特别 简单数据类型 简介 简单数据类型 Number 简单数据类型 String 简单数据类型 Boolean、Undefined、Null 获取类型 类型转换 | …

巴菲特斥资290亿抄底,台积电跌成“白菜价”?

11月14日&#xff0c;巴菲特旗下伯克希尔向美国证券交易委员会&#xff08;SEC&#xff09;提交了13F季度报告。报告显示&#xff0c;三季度伯克希尔斥资41亿美元&#xff08;约290亿人民币&#xff09;大幅买入台积电。 报告发出后&#xff0c;第二天台积电美股涨超6%&#x…

多旋翼无人机组合导航系统-多源信息融合算法(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

GLAD:利用全息图实现加密和解密

概述 全息图能够通过两束相干光相干叠加获得。用其中一束光照射生成的全息图就可以得到另一束相干光&#xff0c;这样全息图就可以用作加密/解密的装置了。 系统描述 在本例中一个复杂的随机图样作为参考光源&#xff0c;用来恢复全息图样对应的物光源。加密过程中&am…

单目标应用:人工兔优化算法(Artificial Rabbits Optimization ,ARO)求解旅行商问题TSP(提供MATLAB代码)

一、算法简介 人工兔优化算法&#xff08;Artificial Rabbits Optimization &#xff0c;ARO&#xff09;由Liying Wang等人于2022年提出&#xff0c;该算法模拟了兔子的生存策略&#xff0c;包括绕道觅食和随机躲藏&#xff0c;并通过能量收缩在两种策略之间转换。绕道觅食策…

显示订单列表【项目 商城】

显示订单列表【项目 商城】前言显示订单列表1 持久层1.1 规划SQL语句1.2 实现接口与抽象方法1.3 配置SQL映射测试2 业务层2.1 规划异常2.2 编写接口与抽象方法2.3 实现抽象方法测试3 控制器3.1 处理异常3.2 设计请求3.3 处理请求测试4 前端页面测试前言 写作于 2022-10-14 17:…

【MySQL】安装与配置(内附安装包+未将对象引用设置到对象的实例的错误解决方法)

目录 一、数据库分类 &#xff08;1&#xff09;关系型数据库&#xff08;RDBMS&#xff09; &#xff08;2&#xff09;非关系型数据库 二、MySQL服务器安装 三、安装包文件分享 一、数据库分类 数据库大体可以分为关系型数据库和非关系型数据库 &#xff08;1&#xff0…

U盘复制错误0x80071ac3如何解决?

U盘是一款移动存储设备&#xff0c;但是在使用中也会遇到一些错误问题&#xff0c;比如文件复制、粘贴或移动时提示0x80071ac3错误代码要如何解决呢&#xff1f;下面就和小编一起来看看解决办法吧。 方法一&#xff1a; 1、有些用户是使用U盘时出现的问题&#xff0c;先按下快捷…

记宝塔使用webhook自动化同步gitee代码

1、服务器ssh密钥 1.1、输入命令查看服务器是否存在密钥&#xff1a; cd ~/.sshls id_xxx.pub的是公钥、id_xxx的是私钥 如果没有&#xff0c;就要先生成一下&#xff0c;生成ssh密钥参考https://gitee.com/help/articles/4181#article-header0 1.2、复制ssh公钥到码云公钥…

【Hack The Box】linux练习-- Blocky

HTB 学习笔记 【Hack The Box】linux练习-- Blocky &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月17日&#x1f334; &#x1f3…

UE4 回合游戏项目 22- 添加第二个玩家

在上一节&#xff08;UE4 回合游戏项目 21- 添加多种类型的敌人&#xff09;基础上新添加一个玩家角色 效果&#xff1a; 步骤&#xff1a; 1.打开进阶游戏资源&#xff0c;解压“回合迁移_第七节&#xff08;只是新人物包&#xff09;” 2.解压后双击打开工程 3.选中“ziyuan…

如何通过快解析实现外网远程访问JupyterNotebook

什么是Jupyter Notebook&#xff1f;官网介绍&#xff1a;Jupyter Notebook是基于网页的用于交互计算的应用程序。其可被应用于全过程计算&#xff1a;开发、文档编写、运行代码和展示结果。简单地说&#xff0c;Jupyter Notebook是以网页的形式打开&#xff0c;可以在网页页面…

Spring Boot——yml和properties详解

文章目录1. 配置文件作用2. 配置文件的格式和分类2.1 规则&#xff08;tips&#xff09;2.2 为配置文件安装提示插件3. properties 配置文件说明3.1 properties 基本语法3.2 关于 properties 中文乱码的问题处理&#xff1a;4. 读取 properties 配置文件4.1 读取单个配置文件5.…