04 | 戏说单例设计模式

单例设计模式

目的:保证一个类仅有一个实例,并提供一个全局的访问点。

类型:创建型

使用场景:确保任何情况下绝对仅有一个实例(线程池、数据库连接池)

优点:

  • 仅有一个实例,减少内存开销
  • 避免对资源文件多重占用严格控制访问

缺点:

  • 无接口
  • 拓展困难

重点:

  • 私有构造器
  • 线程安全
  • 延迟加载
  • 序列化和反序列化安全
  • 反射攻击

单例模式:懒汉式、饿汉式、静态内部类、枚举

懒汉式

public class LazySingleton {
    /**
     * 初始化懒加载
     */
    private static LazySingleton lazySingleton = null;

    /**
     * 私有构造方法
     */
    private LazySingleton(){

    }

    /**
     * 静态方法public方法,获得实例
     * @return lazySingleton
     */
    public static LazySingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

}

单线程可用,但多线程会破坏单例契约,线程不安全

public class LazySingletonTest {
    /**
     * 检测线程是否安全
     */

    public static void main( String[] args ) {
        //    LazySingleton lazySingleton = new LazySingleton(); 私有构造方法无法创建实例
        Thread t1 = new Thread( new T() );
        Thread t2 = new Thread( new T() );
        t1.start();
        t2.start();
        System.out.println("Program End...");
    }


}
public class T implements Runnable {
    @Override
    public void run() {
        LazySingleton lazySingleton = LazySingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+ "  "+lazySingleton);
    }
}

Debug调试,令两个线程都溜入该单例模式的20行中,闷声发大财。两个线程均产生各自的对象

Thread-0  singleton.LazySingleton@15082754
Thread-1  singleton.LazySingleton@16fea746
Program End...

改进:在方法中添加synchronized,锁定整个类,当一个线程创建单例时,会阻塞其他线程进入该类
synchronized:

  • 加在到静态方法中,锁定整个class文件
  • 加在非静态方法中,锁的是堆内存中的对象
public synchronized static LazySingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

public static LazySingleton getInstance(){
        synchronized(LazySingleton.class){
            if(lazySingleton == null){
                lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }

懒汉式进化—Double Check Lazy Singleton

public class LazyDoubleCheckSingleton {
    /**
     * volatile解决由重排序而产生的空指针异常,volatile中不允许重排序
     */
    private static volatile LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;

    private LazyDoubleCheckSingleton(){

    }

    public static LazyDoubleCheckSingleton getInstance(){
        if(lazyDoubleCheckSingleton == null){
            synchronized ( LazyDoubleCheckSingleton.class ){
                if(lazyDoubleCheckSingleton == null){
                    /*
                     * 1.给对象分配内存
                     * 2.初始化对象
                     * 3.设置lazyDoubleCheckSingleton指向刚分配的内存地址
                     * 所有线程在执行java程序时必须要遵守intra-thread semantics
                               * 保证重排序不会改变单线程内的排序结果
                     *
                     */
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

性能优化:

  • 添加判断,减少synchronize锁的使用
  • volatile修饰变量,解决由重排序而引起的空指针异常,解决方式是不允许重排序

静态内部类

public class StaticInnerClassSingleton {
    /**
     * 私有构造方法
     */
    private StaticInnerClassSingleton(){

    }

    /**
     * 静态内部类
     */
    private static class InnerClass{
    private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    /**
     * 获取方法
     * @return Instance
     */
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }

}

原理:

  • 类初始化时将获得初始化锁,使得对象初始化的过程(重排序)对其他线程不可见。
  • 基于类初始化的延迟单例解决方案

饿汉式

饿汉式单例模式:类加载时就初始化完成,可以用final修饰,可用静态代码块的方式创建实例

public class HungrySingleton {
    /**
     * 创建实例,直接初始化
     */
//    private static final HungrySingleton hungrySingleton = new HungrySingleton();
    private static final HungrySingleton hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }

    /**
     * 构造方法私有
     */
    private HungrySingleton(){

    }

    /**
     * 单例方法
     */
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}
  • 浪费资源
  • 线程安全

枚举类

序列化和反序列化破坏单例模式

反射破坏单例模式:无参构造方法的反射破坏——懒汉式无法防御反射防御

猜你喜欢

转载自www.cnblogs.com/xm08030623/p/12418377.html