侧边栏壁纸
博主头像
程序员Jaye博主等级

但行好事,莫问前程

  • 累计撰写 15 篇文章
  • 累计创建 12 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

单例模式

codejaye
2023-06-03 / 0 评论 / 0 点赞 / 43 阅读 / 6421 字

单例模式singleton

保证一个类只有一个实例 并且提供一个访问该实例的全局访问点

主要

饿汉式( 线程安全调用效率高。但是,不能延时加载。) 懒汉式( 线程安全调用效率不高。但是,可以延时加载。) 其他 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用) 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) 枚举单例(线程安全,调用效率高,不能延时加载)

饿汉式

饿汉式 类加载到内存后,就实例化一个单例,JVM保证线程安全 简单实用,推荐实用! 唯一的缺点,不管用到与否,类装载时就完成实例化 (话说你不用,你装载它干啥) JVM保证每一个class只会load到内存一次,static也是保证class类load到内存后立马写到内存,所以也能保证一次

  • 主体代码
public class Singleton01 {

    private static final Singleton01 INSTANCE = new Singleton01();

    private Singleton01() {
    }

    public static Singleton01 getInstance() {
        return INSTANCE;
    }

}
  • 验证代码
    public static void main(String[] args) {

        Singleton01 m1 = Singleton01.getInstance();
        Singleton01 m2 = Singleton01.getInstance();
        System.out.println(m1 == m2);
    }

懒汉式

  • 主体代码
public class Singleton03 {

    private static Singleton03 INSTANCE;

    private Singleton03() {
    }

    public static Singleton03 getInstance() {
        if (INSTANCE == null) {
            //此处try-catch是测试多线程的代码
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //当 INSTANCE为null的时候再初始化
            INSTANCE = new Singleton03();
        }
        return INSTANCE;
    }



}

  • 验证代码
    /**
     * 线程不安全测试,hashcode不唯一
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new  Thread(()-> System.out.println(Singleton03.getInstance().hashCode())).start();
        }

    }
  • 虽然优化了饿汉式的类用到在加载的需求,但是导致和更加严重的线程问题

懒汉式-加锁

可以通过synchronized解决,但也带来了效率下降

  • 主体代码
public class Singleton03 {

    private static Singleton03 INSTANCE;

    private Singleton03() {
    }

    /**
     * synchronized 加锁
     * @return
     */
    public static synchronized Singleton04 getInstance() {
        if (INSTANCE == null) {
            //此处try-catch是测试多线程的代码
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //当 INSTANCE为null的时候再初始化
            INSTANCE = new Singleton04();
        }
        return INSTANCE;
    }



}

  • 验证代码
    /**
	  * 加锁后线程安全,但是效率降低
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new  Thread(()-> System.out.println(Singleton03.getInstance().hashCode())).start();
        }

    }

懒汉式继续优化

尝试通过减少同步代码块的方式提高效率,然而是不行的


    public static Singleton05 getInstance() {
        if (INSTANCE == null) {
            //此处try-catch是测试多线程的代码
            //尝试通过减少同步代码块的方式提高效率,然而是不行的
            synchronized (Singleton05.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //当 INSTANCE为null的时候再初始化
                INSTANCE = new Singleton05();
            }
        }
        return INSTANCE;
    }

    

为啥不行分析 等于没锁

懒汉式双重检查

  • 主体代码
    /**
     * 此处需要加上volatile
     * 涉及到指令重拍,会产生问题
     */
    private static volatile Singleton06 INSTANCE;

    private Singleton06() {
    }

    public static Singleton06 getInstance() {
        if (INSTANCE == null) {
            //此处try-catch是测试多线程的代码
            synchronized (Singleton06.class) {
                if (INSTANCE==null){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    //当 INSTANCE为null的时候再初始化
                    INSTANCE = new Singleton06();
                }
            }
        }
        return INSTANCE;
    }

懒汉式静态内部类-优秀写法

  • 主体代码
public class Singleton07 {


    private Singleton07() {
    }

    /**
     * 你在初始化类的时候,加载Singleton07是不会加载Singleton07Holder的 
     * 加载的线程由JVM自身去保证
     */
    private static class Singleton07Holder{
        //因为构造方式是 private ,只有内部的类才可以new出来,外面的new不出来
        private final static Singleton07 INSTANCE =new Singleton07();
    }

    /**
     * 调用getInstance才会加载Singleton07Holder
     * @return
     */
    public static Singleton07 getInstance(){
        return Singleton07Holder.INSTANCE;
    }


}

  • 验证代码
    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(Singleton07.getInstance().hashCode())).start();
        }

    }

懒汉式(Effective Java中)写到(完美)

  • 主体代码
/**
 * Effective Java
 * Java之父编写的单例模式,懒汉式
 * 不仅可以解决线程同步问题,还可以繁殖反序列化问题
 */
public enum Singleton08 {

    //枚举类没有构造方法,所以不会被反序列化
    INSTANCE;

}

  • 验证代码
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    System.out.println(Singleton08.INSTANCE.hashCode())
            ).start();
        }

    }

参考

https://www.bilibili.com/video/BV1Qb4y1h7fc?p=2

0

评论区