单例模式笔记

单例模式


  • 多线程的线程池的底层设计一般采用单例模式。

  • web应用的配置对象采用单例模式,因为配置文件是共享的资源。

  • 网站的计数器,应用的计数器。

  • 应用程序的日志应用一般采用单例模式,由于共享的日志文件一直处于打开状态,所以只能有一个实例去操作。

  • 数据库的连接池的设计一般采用单例模式,数据库资源的打开或关闭非常消耗资源,所以用单线程维护。

    单例模式的七种写法


    第一种 懒汉式,线程不安全

    定义一个指针,使用时用 getInstance 方法获取,线程不安全

    public class Singleton {
        private Singleton instance;
      private Single(){}
        public static Singleton getInstance(){
          if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    第二种 懒汉式,线程安全

    适合多线程情况,但是效率低

    public class Singleton{
        private static Singleton instance;
        private Single(){}
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    第三种 饿汉式

    这种方式基于classloader机制避免了多线程的同步问题,没有lazy loading

    public class Singleton{
        private static Singleton instance = new Singleton();
        private Singleton (){}
        public static Singleton getInstance(){
            return instance;
        }
    }
    
    第四种 饿汉式2.0

    类似第三种,在类初始化时就实例化instance

    public class Singleton{
        static {
            instance = new Singleton();
        }
        private Singleton(){}
        public static Singleton getInstance(){
            return this.instance;
        }
    }
    
    第五种 静态内部类

    利用 classloader 的机制来保证初始化instance时只有一个线程,跟第三种和第四种的细微差别是:第三种和第四种是只要Singleton被装载了,那么instance就会被实例化(未实现lazy loader),而这种方式是Singleton类被装载了,instance不一定被初始化,因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方式,才会显示装载SingletonHolder类,从而实例化instance。相对于第三四种的方式填补的缺点。

    1. 实例化instance很消耗资源
    2. 不希望在Singleton类装载时就实例化
    public class Singleton{
        private static class SingletonHolder{
            private static final Singleton INSTANCE = new Singleton();
        }
        private Single(){}
        public static final Singleton getInstance(){
    		return SingletonHolder.INSTANCE;
        }
    }
    
    第六种 枚举

    此种方式是Effective Java 作者Josh Bloch 提倡的方式,它可以避免多线程同步问题,并且能够防止反序列化重新创建新的对象,

    public enum Singleton {
        INSTANCE;
        public void whateverMethod(){
        }
    }
    
    第七种 双重校验锁

    第二种的升级版,俗称双重检查锁定

    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;
        }
    }
    
    问题
    1. 如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例,假定不是远端存取,例如一些servlet 使用完全不同的类,装载器,这样会有两个servlet访问单例类
    2. 如果Singleton 实现了java.io.Serializable 的接口,那么这个类的实例就可能被序列化和复原,如果序列化一个,二复原多个,那么就会有多个单例对象
    解决

    1

    private static Class getClass (String classname) throw ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if(classLoader == null) classLoader = Singleton.class.getClassLoader();
        return (classLoader.loadClass(className));
    }
     
    

    2

    public class Singleton implements java.io.Serializable {
        public static Singleton INSTANCE = new Singleton();
        
        protected Singleton(){}
        private Object readResolve(){
            return INSTANCE;
        }
    }
    
    
    总结

    第四种和第三种类似,第一种不算,所以,一般五种写法,懒汉,饿汉,双重校验锁,枚举,静态内部类。

posted @ 2021-03-31 16:14  xido  阅读(47)  评论(0编辑  收藏  举报