程序员想法

设计模式之单例模式,每个人都是唯一

2019-04-30  本文已影响2人  java程序员思维

单例模式

单例模式(SingletonPattern)是 Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式解决的问题

保证一个类仅有一个实例,并提供一个访问它的全局访问点。防止一个全局使用的类频繁地创建与销毁,节省资源,方便管理。

单例模式注意事项

单例类只能有一个实例。单例类必须自己创建自己的唯一实例,其他任何地方无法通过构造方法创建对象,也意味着需要构造方法私有化。单例类必须给所有其他对象提供这一实例。

使用场景

网站的计数器,一般也是采用单例模式实现,否则难以同步。

数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。

操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

代码实现

单例模式又分为懒汉模式和饿汉模式,两种的区别在于是不是启动的时候创建对象,饿汉模式不管是否需要对象,都先创建好。懒汉模式是在需要对象的时候,进行对象的创建。本质区别是时间和空间的取舍,懒汉模式是用时间换空间,启动时候不需要创建对象,节省了空间,但是访问对象的时候要判断是否已经创建对象,会浪费一些时间。饿汉模式是用空间换时间,启动的时候创建对象,浪费了一些空间,但是访问的时候,不需要创建对象和判断对象是否存在,节省了时间,提高了效率。

//懒汉模式

public class Singleton {

    private Singleton() {

    }

    private static Singleton instance = null;

    /* 静态工程方法,创建实例  缺点非线程安全*/

    public static Singleton getInstance() {

        if (instance == null) {

            instance = new Singleton();

        }

        return instance;

    }

}

public class SingletonHungry {

    //构造器私有化

    private SingletonHungry(){}

    private static SingletonHungry instance = new SingletonHungry();

    /* 静态工程方法,创建实例*/

    public static SingletonHungry getInstance() {

        return instance;

    }

}

上面的懒汉模式存在一些问题,在多线程的情况下,多个线程同时调用getInstance方法,可能会创建多个对象,违背了单例模式只有一个实例的原则,需要对getInstance进行同步处理。

public class Singleton {

    private Singleton() {

    }

    private static Singleton instance = null;

    public static synchronized Singleton getInstance() {

        if (instance == null) {

            instance = new Singleton();

        }

        return instance;

    }

}

虽然上面的代码可以保证只会创建一个单例,但是效率很低,是对整个getInstance方法加锁,一旦对象已经创建,每次只能有一个线程访问对象,可以通过双检锁的方式进行优化,既可以保证只会创建一个对象,同时又允许多个线程访问实例。

public class Singleton {

    private Singleton() {

    }

    private volatile static Singleton instance = null;

    public static Singleton getInstance() {

        if (instance == null) {

            synchronized (instance) {

                if (instance == null) {

                    instance = new Singleton();

                }

            }

        }

        return instance;

    }

}

使用volatile修饰instance属性,保证属性的可见性,只要发生变化对所有线程可见,因为同步代码范围变小,可以提高效率。双检锁可以保证只会创建一个对象实例。

保证单例,还有很多方式,枚举自身是线程安全的,也是一种不错的选择。

public enum Signleton {

    INSTANCE;

    private Something something;

    Signleton() {

        something = new Something();

    }

    public Something getSomething() {

        return something;

    }

}

优缺点

优点:在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。由于在系统内存中只存在一个对象,因此可以 节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 

缺点:不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。 单例类的职责过重,在一定程度上违背了“单一职责原则”。 

我的启发

世界上没有两片完全相同的树叶,人何尝不是如此,每个人都是唯一,做最真实的自我。我是幸运的,因为我还活着,珍爱生命,过好每一天。

上一篇 下一篇

猜你喜欢

热点阅读