首页 > 其他 > 详细

单例设计模式

时间:2019-12-13 17:47:34      阅读:103      评论:0      收藏:0      [点我收藏+]

单例设计模式

保证一个类仅有一个实例,并提供一个全局的访问点。
类型:创建型
使用场景:确保任何情况下绝对仅有一个实例
线程池、数据库连接池

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

缺点:无接口,拓展困难

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

单例模式:懒汉式、饿汉式

懒汉式单例设计模式

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;
    }
}
  • 浪费资源
  • 线程安全

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

反射防御

枚举类实现单例模式

单例设计模式

原文:https://www.cnblogs.com/xm08030623/p/12036225.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!