
学会设计模式,你就可以像拥有魔法一样,在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们(GoF)凝聚出来的最佳实践,可以提高代码的可读性、可维护性和可重用性,从而让我们的开发效率更高。通过不断的练习和实践,掌握其中的奥妙,选择合适的设计模式,能为我们的项目增加一丝神奇的魔力。
文章目录
- 实例:
 - 目的:
 - 适用场景:
 - 优点:
 - 弊端:
 - 类图:
 - 框架用到的地方:
 - Coding:
 - 线程不安全
 - 将getInstance()方法进行改造
 - 测试:
 - 测试结果:
 
- 双检锁方式——线程安全
 - 改造getinstance()方法
 - 测试
 - 测试结果
 
- 利用枚举
 - 将饿汉和懒汉结合:
 - 测试
 - 测试结果:
 
实例:
模拟多线程下单例模式(懒汉式)初始化对象
目的:
使得一个全局使用的类不会被频繁地创建与销毁。
适用场景:
1、需要频繁地进行创建和销毁的对象
 2、创建对象耗时或耗费资源过多,但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。
优点:
在类加载时不初始化,使用时才创建,因此类加载速度快,节约资源
弊端:
运行时获取对象的速度慢。在多线程的场景下,为了实现线程安全,要付出额外代价
类图:

框架用到的地方:
java.lang.Runtime;spring依赖注入
Coding:
线程不安全
/**
 * 线程不安全
 */
public class LazySingleton {
    private static LazySingleton instance = null;
    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
    public void showMsg() {
        System.out.println("This is LazySingleton!");
    }
}
 
这种方式,不能保证线程安全,接下来我们模拟一个多线程场景:
将getInstance()方法进行改造
public static LazySingleton getInstance() {
    if (instance == null) {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        instance = new LazySingleton();
    }
    return instance;
}
 
测试:
@Test
public void Test1() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(LazySingleton.getInstance());
        }
    }).start();
    System.out.println(LazySingleton.getInstance());
}
 
测试结果:
LazySingleton@48cf768c
LazySingleton@47382356
 
上述代码之所以说它“线程不安全”是因为:在多个线程同时调用getInstance()方法的时候,大概率会同时创建多个实例,所以此时我们不得不加上“synchronized”来避免这个问题,但是我们可以很快的发现:假设多个线程都在竞争锁,又会严重影响并发性能,所以我们只需要在竞争锁之前先判断一次实例是否此时为null,就可以减少不必要的抢锁动作,至此,我们就完成了“双检锁方式”,这是一种比较好的单例实现模式。
双检锁方式——线程安全
/**
 * 双检锁方式
 * 线程安全
 */
public class LazySingletonV2 {
    private static LazySingletonV2 instance = null;
    private LazySingletonV2() {
    }
    public static LazySingletonV2 getInstance() {
        //如果instance不为null,无需抢锁,返回instance即可
        if (instance == null) {
            //避免多线程调用时,创建多个instance
            synchronized (LazySingletonV2.class) {
                //抢到锁的线程判断是否为null
                if (instance == null) {
                    instance = new LazySingletonV2();
                }
            }
        }
        return instance;
    }
    public void showMsg() {
        System.out.println("This is LazySingletonV2!");
    }
}
 
synchronized:等一个人执行完毕,才会继续执行
 模仿上面的实验,我们再做一次:
改造getinstance()方法
public static LazySingletonV2 getInstance() {
    //如果instance不为null,无需抢锁,返回instance即可
    if (instance == null) {
        //避免多线程调用时,创建多个instance≈
        synchronized (LazySingletonV2.class) {
            try {
                //模拟创建对象之前做的准备工作
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //抢到锁的线程判断是否为null
            if (instance == null) {
                instance = new LazySingletonV2();
            }
        }
    }
    return instance;
}
 
测试
@Test
public void Test2() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(LazySingletonV2.getInstance());
        }
    }).start();
    System.out.println(LazySingletonV2.getInstance());
}
 
测试结果
LazySingletonV2@5a420d92
LazySingletonV2@5a420d92
 
当然我们也可以直接利用Java内置的“枚举”来实现单例模式:因为枚举类型是线程安全的,并且只会装载一次。
利用枚举
/**
 * 利用枚举
 */
public enum LazySingletonV3 {
    INSTANCE;
    private String Msg = "This is LazySingletonV3!";
    public String getMsg() {
        return Msg;
    }
    public void setMsg(String msg) {
        Msg = msg;
    }
}
 
将饿汉和懒汉结合:
/**
 * 饿汉,懒汉结合
 */
public class LazySingletonV4 {
    public static class InnerHolder{
        private static LazySingletonV4 instance = new LazySingletonV4();
    }
    private LazySingletonV4() {
        System.out.println("LazySingletonV4"+"create");
    }
    public static LazySingletonV4 getInstance() {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        return InnerHolder.instance;
    }
    public void showMsg() {
        System.out.println("This is LazySingleton!");
    }
}
 
测试
@Test
public void Test4() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(LazySingletonV4.getInstance());
        }
    }).start();
    System.out.println(LazySingletonV4.getInstance());
}
 
测试结果:
LazySingletonV4  getInstance
LazySingletonV4  getInstance
LazySingletonV4  create
LazySingletonV4@48cf768c
LazySingletonV4@48cf768c
 
从打印语句可以看出:输出了两次getInstance之后才输出了create,所以只有使用到这个内部类的时候才会被创建
 且由java虚拟机创建,是单线程执行,保证了线程安全
 这种方式结合了懒汉模式和饿汉模式的优点:高效解决了懒汉模式的线程不安全的问题,同时也不会像饿汉模式一样浪费资源。



















