【java设计模式】——浅谈设计模式 单例模式

在我们平时的编程过程中,可能敲过代码最多的就是new Xxx()了,因为这也是我们平时面向对象编程的固有套路。(虽然还能通过反射来创建一个类的实例)。但大家知否在一些场景中遇到: 某些情况下我只希望当前类只有一个实例? 也就是在内存中只有一个这样的对象。例如在《大话设计模式》中的那句话:“有些类也需要计划生育”。可能有些人在这里就会有疑问了,我只要new一个当前类的对象不就完事了吗?或者在需要该类对象的类中直接写一个这样的对象不也可以吗?
但是你能保证该类的对象在其他类中不能创建第二个对象吗?
那么在设计模式中就有这样的一种解决方法:单例模式。
三句话概括单例模式的关键点:1.该类的对象不允许在其他类中创建(构造器私有);2.该类持有自己类的实例;3.该类提供静态方法,供其他类获取该类自身的实例,即获取(2)。
见以下代码:

public class SingleClass{
    private static SingleClass instance = new SingleClass();

    private SingleClass(){
    }

    public static SingleClass getInstance(){
        return instance;
    }
    someOtherMethod ...
}

对比上面代码,似乎就能看出一二。这个类构造器私有,并未提供公共构造器,这样就保证别的类中不能通过new SingleClass();创建对象。该类持有一个自己的对象。,唯一能访问操作该类对象的途径就是通过它的静态方法getInstance。那么这样就能保证该类不管在哪里使用,都只含有一个当前类的对象了。
这样极大满足了控制欲强的那批人的愿望————只能有唯一。爽不爽?
还不够不爽!
当 当前类中的对象变成静态的时候,在该类类加载的同时就已经在内存中存在了该类的实例了。这样不管你用不用我都有了,占用了内存,提前消耗系统资源。
能不能把实例创建推后在我需要的时候呢?
能,你看:

public class SingleClass{
    private static SingleClass instance;

    private SingleClass(){
    }

    public static SingleClass getInstance(){
        if(instance == null){
            instance = new SingleClass();
        }
        return instance;
    }
}

这样是不是满足了要求呢?在我需要这个对象都时候在创建它。但细心的小伙伴发现了问题。如果是多线程环境下,两个线程同时调用了getInstance方法。在调用之前线程一发现instance为空,同时,线程二也执行到了此处,这是线程一完成了初始化,其后线程二再次初始化。造成变量两次初始化。
解决的方法:线程锁!
再看一段代码:

public static SingleClass getInstance(){
        synchronized (SingleClass.class) {
            if(instance == null){
                instance = new SingleClass();
            }
            return instance;
        }
    }

这样改写静态的getInstance方法可好呢?
其实也并不咋滴,效率太低了。我可能只需要在实例化的时候给临界区加锁,但现在不管你有没有实例化,两个线程遇见了就会阻塞掉了。
怎么办才好呢?
最后直接上代码看吧筒子们:

public static SingleClass getInstance(){
        if(instance == null){
            synchronized (SingleClass.class) {
                if(instance == null){
                    instance = new SingleClass();
                }
            }
        }
        return instance;
    }

看,这样在奔雷对象实例化之后,直接返回对象的引用。在初始化之前,如果存在线程安全问题,加锁判断,就能保证实例化一次!

两次的代码是在不断的演化过程中改进的。每一次都会对之前的代码探索、优化、再探索、再优化。直接在类加载中实例化的那个是“懒汉式”,不管三七二十一先给你实例化。在通过方法访问对象再初始化是“饿汉式”。懒汉消耗资源,饿汉有线程安全问题。在学习理解之后要权衡利弊。适地使用。
由于 继承 后存在的问题也可在后续评论区讨论,欢迎留言指点。

你可能感兴趣的:(设计模式)