zoukankan      html  css  js  c++  java
  • 单例设计

    常见的单例设计模式有以下7种
     
    1.懒汉  线程不安全
    public class Singleton {
        private static Singleton instance;
        private Singleton(){}
        public static Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    } 

    所谓懒汉 , 就是初始化类的时候不创建实例 , 什么时候使用再创建
    类似于一种懒加载的机制
    但是由于没有线程安全的保障 , 不同的线程很可能会获得不同的实例
    所以并不能算是真正严格的单例模式
     
    2.懒汉  线程安全
    与第一种形式类似 , 只需要在getInstance方法上面加上 synchronized修饰即可
    保证了不同的线程只能拿到唯一的实例 , 实现了线程安全和懒加载
    但是效率比较低 , 因为在很多的情况下是不需要同步的 
     
    3.饿汉
    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton(){}
        public static Singleton getInstance(){
            return instance;
        }
    }

    这种方式基于类加载器的机制 , 关于类加载器在后面的笔记里面再做介绍
    有效避免了多线程的同步问题 , 但是并没有绝对实现懒加载
    ( 在这个类被装载的时候才会创建该单例对象
    但是导致类被装载的原因可以有多种 , 比如类中的其他静态方法被调用
    并不一定是调用了getInstance方法 )
     
    4.饿汉 ( 变种 )
    与第三种形式类似 , 只是把对象的创建放在static静态子句中进行
    但是实际效果并没有本质的区别
    同样是在类被装载的时候创建该单例对象
     
    5.静态内部类
    public class Singleton {
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        private Singleton(){}
        public static final Singleton getInstance(){
            return SingletonHolder.INSTANCE;
        }
    }

    将这个实例包装在一个静态内部类当中 , 实际的效果就是
    当Singleton类被装载的时候 , 其中的静态内部类并不会被立即装载
    而只有在调用getInstance方法的需要使用这个静态内部类的属性的时候 , SingletonHolder才会被装载
    这就彻底解决了饿汉模式所没有彻底解决的懒加载问题
     
    6.枚举
    枚举本身就是单例的一种扩展
    它是线程安全的 , 而且也可以防止反序列化的时候重新创建单例对象 ( 反序列化问题后面会提到 )
    可以说是一种最完美的方式
    但是枚举这种十分特殊的类平时很少用到 , 实际使用起来可能略有些生疏 
    public enum Singleton {
        INSTANCE;
        //这里也可以写一些这个类的其他方法..
    }

    7.双重校验锁

    public class Singleton {
        private volatile static Singleton singleton;
        private Singleton() {}
    
        public static Singleton getSingleton() {
            if (singleton == null) {
                    synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    } 

    使用双重校验锁可以解决线程安全的懒汉模式所带来的性能问题
    因为当实例已经创建的时候 , 就不需要进入synchronized代码块
     

    序列化与反序列化对单例的破坏
    如果一个类实现了Serializable接口 , 那么它的对象就可以被序列化写入到一个文件当中
    同样也可以读取这个文件重新获取这个类的对象
    但是如果作为一个单例 , 当执行反序列化的时候 , 我们希望拿到的还是这个唯一的对象
    但是实际情况是会重新创建出一个对象 
    public static void main(String[] args)
                throws IOException, ClassNotFoundException{
        FileOutputStream fos = new FileOutputStream("tempFile");
        ObjectOutputStream output = new ObjectOutputStream(fos);
        //将对象序列化写出到文件
        output.writeObject(Singleton.getInstance());
        output.close();
        //从文件中读取内容反序列化为对象
        FileInputStream fis = new FileInputStream("tempFile");
        ObjectInputStream input = new ObjectInputStream(fis);
        Singleton newSingleton = (Singleton) input.readObject();
        System.out.println(newSingleton == Singleton.getInstance());
        /*比较获得的结果是false,代表这是两个不同的对象*/
        input.close();
        //删除临时文件
        File file = new File("tempFile");
        if(file.delete()){
            System.out.println("文件删除成功");
        }
    } 

    解决的办法很简单
    就是在这个单例对应的类当中添加一个方法 
    或者直接使用上面提到的枚举来构造单例
  • 相关阅读:
    java实现第六届蓝桥杯密文搜索
    java实现第六届蓝桥杯奇怪的数列
    jquery input 赋值和取值
    jQuery对html元素的取值与赋值实例详解
    Jmeter接口测试图文示例
    Jmeter接口测试案例实践(一)
    消息队列-推/拉模式学习 & ActiveMQ及JMS学习
    IDEA和Pycharm 等系列产品激活激活方法和激活码
    Mybatis中的association用法
    设置 Tomcat 的JVM运行内存
  • 原文地址:https://www.cnblogs.com/programInit/p/6363176.html
Copyright © 2011-2022 走看看