单例模式学习心得
简介
- 什么是单例 
 一个类只有一个实例,并且提供一个全局访问点
- 为什么需要单例模式 - 对于一些简单类型对象的创建如:int room = 806 、String school = "edu.zsc.bdic" 这些类型的对象创建和销毁对性能影响不大 
- 有些对象的创建过程是庞大而复杂的,并且这些对象完全是可以复用的,如果频繁的创建和销毁将影响性能,如: - 数据库连接对象 
- 配置对象 
 
 
- 实现单例模式需要考虑那些点 
 1、是否是线程安全的:在多线程并发情况下是否仍能保证获取同一实例
 2、是否支持懒加载:如果在类加载的时候就实例化,万一一直不使用则容易浪费内存,我们希望在调用的时候再实例化,这是更加合理的做法。
 3、是否被反射破环(这种方式属于人为破坏,暂时不考虑)
单例实现方式
单例模式实现方法的通式:1.构造方法私有化 2.提供全局访问点
1.饿汉式
//饿汉式1
public class Singleton1 {
    //将实例声明为静态常量,在类加载的时候就实例化
    private static final Singleton1 instance = new Singleton1();
    // 私有构造函数,防止实例化
    private Singleton1() {
    }
    // 提供全局访问点
    public static Singleton1 getInstance(){
        return instance;
    }
}是否线程安全:√
是否支持懒加载:×
2.懒汉式
//懒汉式1
public class Singleton1 {
    private static Singleton1 instance;
    // 私有构造函数,防止实例化
    private Singleton1() {
        
    }
    public static getInstance(){
        //此处存在线程安全问题,可能多个线程同时进入if语句块
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }
}public class Test {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        Thread t2 = new MyThread();
        t1.start();
        t2.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(Singleton1.getInstance());
    }
}
//两个线程的输出结果
edu.zsc.bdic.singleton2.Singleton1@4e704f06
edu.zsc.bdic.singleton2.Singleton1@65d2066b是否线程安全:×
是否支持懒加载:√
3.懒汉式2(校验锁)
//懒汉式2 
public class Singleton2 {
    /*
        volatile 关键字确保 instance 的可见性和有序性
        它防止了指令重排序可能导致的问题,使得一个线程看到一个未完全初始化的对象
     */
    private static volatile Singleton2 instance;
    private Singleton2() {
        // 私有构造函数,防止实例化
    }
    public static synchronized Singleton1 getInstance(){
        //此处存在线程安全问题,可能多个线程同时进入if语句块 需要加锁,但是会影响性能
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}是否线程安全:√
是否支持懒加载:√
线程需要排队,性能一般
4.懒汉式3(双重校验锁)
//懒汉式3 DCL(双重锁)
public class Singleton2 {
    /*
        volatile 关键字确保 instance 的可见性和有序性
        它防止了指令重排序可能导致的问题,使得一个线程看到一个未完全初始化的对象
     */
    private static volatile Singleton2 instance;
    private Singleton2() {
        // 私有构造函数,防止实例化
    }
    public static Singleton2 getInstance(){
        if (instance == null) {  //一重校验:检查是否已经存在实例,避免进入同步代码块
            synchronized (Singleton2.class){
                if (instance == null) {  //二重校验:避免第一个已经创建了实例的线程释放了锁后,其他线程重复创建实例
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}instance = new Singleton2();
最主要是因为这一行代码不具备原子性
它可以被差分为三部分:
  1.分配内存
  2.初始化对象
  3.设置引用指向堆中的对象地址
jvm重排序的时候会打乱它们的顺序,使一个线程看到一个未完全初始化的对象是否线程安全:√
是否支持懒加载:√
实现比较复杂
5.静态内部类
//静态内部类
public class Singleton1 {
    private Singleton1() {
        // 私有构造函数,防止实例化
    }
    // 静态内部类运行机理:JVM加载类的时候是不会加载静态内部类
    // 只有当内部类的属性/方法被访问的时候才会加载
    private static class SingletonHolder{
        private static final Singleton1 INSTANCE = new Singleton1();
    }
    public static Singleton1 getInstance(){
        return SingletonHolder.INSTANCE;
    }
}这种写法巧妙的利用了jvm加载类的机制:静态内部类不会随着外部类的加载而自动加载。JVM在加载包含静态内部类的外部类时,并不会立即加载静态内部类。只有当静态内部类被显式引用时,JVM才会加载它。
是否线程安全:√
是否支持懒加载:√
心得
单例模式作为设计模式里面一种简单的模式,它包含了线程安全、内存模型、类加载机制等等一些核心的知识点。
认识到了单例模式的几种方式:饿汉式,懒汉式,双重校验锁,静态内部类,也了解到了它们各自的特点。
虽然单例模式是一种简单的设计模式,但是它考验了程序员的基本功是否扎实,在学习的过程中我发现了自己对这方面的了解确实有所欠缺。
参考
https://mp.weixin.qq.com/s/nKsKOIdV-vt-tAEs5mqlpw
B站:BV1pt4y1X7kt单例模式学习心得
        http://localhost:8090//archives/dan-li-mo-shi-xue-xi-xin-de