创建型模式--单例模式

单例模式

  单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

  注意:1)单例类中只能有一个实例

     2)单例类必须自己创建自己的唯一实例

     3)单例类必须给所有其他对象提供这一实例

介绍

  意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  主要解决:一个全局使用的类频繁地创建与销毁。

  何时使用:当您想控制实例数目,节省系统资源的时候。

  如何解决:判断系统是否已经有这个单例,如果有则返回,没有则创建。

  关键代码:构造函数是私有的。

  应用实例:一个班级只有一个班主任。

  优点:1)在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。2)避免对资源的多重占用(比如写文件操作)。

  缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

  应用场景:1)要求生产唯一序列号。2)WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。3)创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

 

实现

  我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo,我们的演示类使用 SingleObject 类来获取 SingleObject 对象。

 

   1、创建一个Singleton 类。

public class SingleObject {

    private static SingleObject instance = new SingleObject();

    private SingleObject(){}

    public static SingleObject getInstance(){
        return instance;
    }

    public void showMessage(){
        System.out.println("hello world!");
    }
}

  2、从 Singleton 类获取唯一的对象。

public class SingletonPatternDemo {

    public static void main(String[] args) {
        //不合法,构造函数不可见
        //SingleObject so = new SingleObject();

        SingleObject so = SingleObject.getInstance();
        so.showMessage();
    }
}

  输出:hello world!

 

单例模式的几种实现方式

  1、懒汉式不加锁,线程不安全。可能会有多个进程进入到 if 语句内,创建多个实例。

/*
* 懒加载,线程不安全。
* */
public class SingletonLazyUnlocked {
    private static SingletonLazyUnlocked instance;

    private SingletonLazyUnlocked(){}

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

  2、懒汉式加锁,线程安全。加synchronized影响效率。

/*
* 懒加载,线程安全。
* */
public class SingletonLazyLocked {
    private static SingletonLazyLocked instance;

    private SingletonLazyLocked(){}

    public static synchronized SingletonLazyLocked getInstance(){
        if(instance != null){
            instance = new SingletonLazyLocked();
        }
        return instance;
    }
}

  3、饿汉式,线程安全。类加载时创建实例,容易产生垃圾对象。

/*
* 非懒加载,线程安全。
* */
public class SingletonHungry {
    private static SingletonHungry instance = new SingletonHungry();

    private SingletonHungry(){}

    public static SingletonHungry getInstance(){
        return instance;
    }
}

  4、双重校验锁(Double-Checked Locking)

/*
* 懒加载,线程安全
* */
public class SingletonDCL {
    private volatile static SingletonDCL instance;

    private SingletonDCL(){}

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

}

  第一次校验:如果创建了实例再去执行getInstance()方法不会去竞争锁,直接返回实例。

  第二次校验:防止二次创建实例。如果没有第二个判空语句,在实例还没有创建的情况下,A 和 B 两个线程都要获得单例的对象。A ,B 都进入了第一个判空语句内,然后争夺锁,A获得了锁,实例化了一个对象,然后释放了锁,B获得了锁,由于没有第二个判空语句B 又创建了一次对象。

  volatile 关键字的作用:防止指令重排优化。保证变量在多线程运行时的可见性,无法保证原子性。

 

  5、静态内部类

  对静态域使用延迟初始化利用类加载机制来保证初始化 instance 时只有一个线程。

/*
* 静态内部类懒加载
* 懒加载,线程安全
* */
public class SingletonRegister {
    private static class SingletonHolder{
        private static final SingletonRegister INSTANCE = new SingletonRegister();
    }

    private SingletonRegister(){}

    public static final SingletonRegister getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

  

  6、枚举

  实现单例的最佳方法。更简洁,自动支持序列化机制防止多次实例化。

public enum SingletonEnum {
    INSTANCE;
    public void method(){

    }
}

 

参考:https://www.runoob.com/design-pattern/singleton-pattern.html

posted @ 2020-03-07 10:10  hoo334  阅读(126)  评论(0编辑  收藏  举报