• 个人感悟之单例模式


      我记得我第一次接触到单例模式的时候,我第一感觉就是这个东西也有丶简单了8,其实我现在看我当初的想法也不觉得奇怪,初次看到单例模式,概念:对于一个类,有且只有一个实例,并向外界提供获取这个实例的入口。当初的我看到这段字,飞速的写完了一段代码,代码如下。

    public class simpleSingleton {
        private static final simpleSingleton singleton = new simpleSingleton();
        private simpleSingleton(){}
        public static simpleSingleton getInstance() {return singleton;}
    }

      其实现在来看,这段代码的确已经初步实现了单例模式,但是后面通过学习,才发现单例模式其中下面还有很多分叉,还有很多坑。我们先从懒汉饿汉开始聊起吧。

      先说饿汉模式(其实上面这段代码就是饿汉模式),就是对于这个类的唯一实例,我不管会不会有人用到它,我先声明出来这个实例,如果有人要用到它就直接调给出的那个接口就好了。但是这样会出现一个资源浪费的问题,如果这个实例我们永远用不到那岂不是很浪费资源,我们要环保杜绝浪费,因此开始考虑饿汉模式。

      我先简单说一下饿汉模式的思想,饿汉模式就是当需要用到这个实例的时候,才会去创建这个实例。这样可以保证在不用到的前提下不会过多的占用系统内存资源。

    public class starvationSingleton {
        private static starvationSingleton singleton = null;
        private starvationSingleton() {}
        public static starvationSingleton getInstance() {
            if(singleton == null) {
                singleton = new starvationSingleton();
            }
            return singleton;
        }
    }

      但是我们用到单例模式的时候并不是都是在单线程环境下运行的,如果出现在多个线程竞争资源的时候,上面的代码就会出现问题。比如说有线程A和线程B,当线程A先判断实例为空时,他就会去声明实例,然后B就判断实例存在了。但是如果A和B同时去判断实例是否为空时,这个问题就出现了,可能他们两个都认为他为空,都去创建了实例,那么单例模式将不复存在。

      怎么去解决这个问题呢?很简单,多线程就要涉及到并发的问题,我们可以锁住某个代码块,当一个线程获取到这个锁的时候,其他的线程必须在外面等待,等到获取锁之后才能访问,这样就解决了线程冲突的问题。

    public class starvationSingleton {
        private static starvationSingleton singleton = null;
        private starvationSingleton() {}
        public static starvationSingleton getInstance() {
            if(singleton == null) {
                synchronized (this) {
                    singleton = new starvationSingleton();
                }
            }
            return singleton;
        }
    }

      之后我还在思考一个问题,我们都知道,Java的反射机制是可以破坏Java类的封装性的,那么Java反射是不是可以让单例模式产生多个实例呢?答案是肯定的,因为我们通过Java反射机制可以获取使用到单例模式中被私有化的构造函数。在某些系统层面上可以用一种技术让非内核类无法使用Java反射机制获取私有构造器,但是这个技术负面效果太多,我们先不考虑。我想到一个办法,就是在线程访问到私有构造器的时候,进行一个判断,如果有实例存在,那么就直接退出这个方法。

      我自己认为这个是个好办法,但是构造器是没有返回值类型的,如果直接返回,程序根本就跑不起来,那么怎么样才能让他终止构造方法的执行呢?可以用到抛异常的方法!代码如下。

    public class starvationSingleton {
        private static starvationSingleton singleton = null;
        private starvationSingleton() {
            if(singleton != null) {
                try {
                    singleton = new starvationSingleton();
                    throw new Exception("不能调用私有构造方法!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        public static starvationSingleton getInstance() {
            if(singleton == null) {
                synchronized (this) {
                    singleton = new starvationSingleton();
                }
            }
            return singleton;
        }
    }
    ------------------------------------------------------------------------------------------------------------------------------------
    之前文章有一些粗心造成的错误现在已经更改,感谢@大福笔记指出
  • 相关阅读:
    《闲扯Redis十》Redis 跳跃表的结构实现
    《闲扯Redis九》Redis五种数据类型之Set型
    《闲扯Redis八》Redis字典的哈希表执行Rehash过程分析
    《闲扯Redis七》Redis字典结构的底层实现
    《闲扯Redis六》Redis五种数据类型之Hash型
    js定时器为什么是不精确的
    单页面应用的优缺点(SPA)
    怎么减少http请求次数
    animation 和 transition 的区别
    akka-typed(9)
  • 原文地址:https://www.cnblogs.com/Jolivan/p/9019338.html
Copyright © 2020-2023  润新知