• 单例模式的几种写法


    单例模式三个要素:

    1.私有构造函数,防止其他代码实例化对象;

    2.提供一个属性,保存那个唯一的实例化对象;

    3.提供一个公共方法,以便别人获取到实例化对象;

    饿汉式

    public class Singleton01 {
        // 私有化构造函数
        private Singleton01() {}
        // 静态属性,指向单例对象
        private static final Singleton01 INSTANCE = new Singleton01();
        //公共方法,以便别人获取对象
        public static Singleton01 getInstance() {
            return INSTANCE;
        }
    }
    

    优点:写法简单,线程安全。

    缺点:消耗资源,不管对象是否会用到,都会实例化对象出来。

    懒汉式

    public class Singleton02{
        // 私有化构造函数
        private Singleton02(){}
        // 静态属性,指向单例对象
        private final final Singleton02 INSTANCE;
        // 公共方法,以便别人获取对象
        public static Singleton02 getInstance(){
            if(INSTANCE == null){
                INSTANCE = new Singleton02();
            }
            return INSTANCE;
        }
    }
    

    优点:写法简单,节省资源,只有用到这个对象的时候才会进行实例化。

    缺点:存在线程安全问题。

    简单加锁

    public class Singleton02 {
        // 私有化构造函数
        private Singleton02() {}
    	// 静态属性,指向单例对象
        private static Singleton02 INSTANCE;
        // 加了synchronized关键字
        public synchronized static Singleton02 getINSTANCE() {
            if (INSTANCE == null) {
                INSTANCE = new Singleton02();
            }
            return INSTANCE;
        }
    }
    

    优点:线程安全,写法简单。

    缺点:每次获取对象都进行加锁,存在性能问题。

    双重检查

    public class Singleton03{
        // 私有化构造函数
        private Singleton03() {}
    	// 静态属性,指向单例对象(注意这里加了volatile关键字,否则在多线程情况下指令重排会出问题)
        private static volatile Singleton03 INSTANCE;
        public static Singleton03 getINSTANCE() {
            // 只有在创建新对象的时候才进行加锁
            if (INSTANCE == null) {
                synchronized (Singleton03.class){
                    // 这里还要再判断一下,否则锁释放之后其他线程还是要实例化对象
                     if (INSTANCE == null){
                         INSTANCE = new Singleton03();
                     }
                }
           }
           return INSTANCE;
        }
    }
    

    优点:线程安全,相比上一种节约资源

    缺点:已经很美好了,已经大大减少了获取锁的次数,不过还是可以更好。

    静态内部类

    public class Singleton04 {
        // 私有化构造函数
        private Singleton04() {}
        // 在内部类声明一个静态属性
        private static class InnerHolder {
            static final Singleton04 INSTANCE = new Singleton04();
        }
        // 公共方法
        public static Singleton04 getInstance() {
            return InnerHolder.INSTANCE;
        }
    }
    

    因为JVM保证了内部类的线程安全,即一个内部类在整个程序中不会被重复加载,并且如果你没有使用到内部类的话,是不会加载这个内部类的。这就非常巧妙的实现了线程安全以及节约资源的好处!

    优点:节约资源、线程安全、性能好

    缺点:会被序列化或者反射破坏单例

    以上的几种写法均存在被序列化或反射破坏的情况,可以进行一下优化

    public class Main implements Serializable, Cloneable {
        private static final long serialVersionUID = 6125990676610180062L;
        private static Main main;
        private static boolean isFirstCreate = true;
        private Main(){
            /*防止反射破坏单例*/
            if(isFirstCreate){
                synchronized (Main.class) {
                    if(isFirstCreate){
                        isFirstCreate = false;
                    }
                }
            }else{
                throw new RuntimeException("已经实例化一次, 不能再实例化");
            }
        }
    
        public  static Main getInstance(){
            if (main == null) {
                synchronized (Main.class) {
                    if (main == null) {
                        main = new Main();
                    }
                }
            }
            return main;
        }
        /*防止克隆破坏单例*/
        @Override
        protected Object clone() throws CloneNotSupportedException {
            // TODO Auto-generated method stub
            return main;
        }
        /*防止序列化破坏单例*/
        private Object readResolve() {
            // TODO Auto-generated method stub
            return main;
        }
    
    
        public static void main(String[] args) throws Exception{
            Main main = Main.getInstance();
            System.out.println("singleton的hashCode:"+main.hashCode());
            //通过克隆获取
            Main clob = (Main) Main.getInstance().clone();
            System.out.println("clob的hashCode:"+clob.hashCode());
            //通过序列化,反序列化获取
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(Main.getInstance());
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            Main serialize = (Main) ois.readObject();
            if (ois != null) ois.close();
            if (bis != null) bis.close();
            if (oos != null) oos.close();
            if (bos != null) bos.close();
            System.out.println("serialize的hashCode:"+serialize.hashCode());
            //通过反射获取
            Constructor<Main> constructor = Main.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Main reflex = constructor.newInstance();
            System.out.println("reflex的hashCode:"+reflex.hashCode());
        }
    }
    

    枚举实现

    一种简单巧妙的方法,根本不存在构造方法,当然也就不会被反射和序列化破坏

    public enum Singleton05 {
        // 实例
        INSTANCE;
        // 公共方法
        public Singleton05 getInstance() {
            return INSTANCE;
        }
    }
    
  • 相关阅读:
    Servlet细节
    https协议
    常用css缩写
    Servlet
    计算机编码总结
    java 中 finally 语句块的 深度解析 总结
    使用DIV之后 table何去何从
    http协议
    web开发基础
    js闭包之我见
  • 原文地址:https://www.cnblogs.com/hanstrovsky/p/14168209.html
Copyright © 2020-2023  润新知