个人感悟之单例模式

  我记得我第一次接触到单例模式的时候,我第一感觉就是这个东西也有丶简单了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;
    }
}
------------------------------------------------------------------------------------------------------------------------------------
之前文章有一些粗心造成的错误现在已经更改,感谢@大福笔记指出
原文地址:https://www.cnblogs.com/Jolivan/p/9019338.html