Java设计模式5:单例模式(Singleton)

Singleton模式

意图

保证一个类仅有一个实例,并提供一个访问他的全局访问点

How

我们怎么保证一个类只有一个实例并且这个实例易于被访问呢?一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。解决方案就是,我们可以让类本身负责他的唯一实例的访问保存,通过拦截创建实例的请求,判断是否有已经存在的实例,没有就创建实例,有则直接返回实例,这就是单例模式。

适用性

1、当类只能有一个实例且客户可以从一个全局的访问点访问他

2、当这个唯一实例应该是通过子类化可扩展的,比拿给客户应该无需修改代码就能使用一个扩展的实例时

常见的实例

线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序等对象常常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler(打印假脱机),以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,单例模式就是为了避免不一致状态,避免政出多门。

结构

这里写图片描述

Singleton

定义一个Instance操作,允许客户访问他的唯一实例。Instance是一个类操作

可能负责创建他自己的唯一实例

优点

1、对唯一实例的受控访问

2、缩小名空间

3、允许对操作和表示的精化

4、允许可变数目的实例

5、比类操作更灵活

实现

1、饿汉式

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 * 1、饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,
 * 除非系统重启,否则这个对象不会改变,本身线程安全。
 * 
 * 2、通过为private的构造方法避免了类在外部被实例化,在同一个虚
 * 拟机范围内,单例模式唯一实例只能通过getInstance()方法访问。
 */
public class HungrySingleton {
    private HungrySingleton() {}

    private static HungrySingleton singleton = new HungrySingleton();

    public static HungrySingleton getInstance() {
        return singleton;
    }

}

2、懒汉式

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 * 1、延迟加载方式,只有在调用getInstance()方法时才创建
 *
 * 2、多线程环境下会产生多个single对象,因此线程不安全
 */
public class LazySingleton {
    private LazySingleton() {}

    private static LazySingleton singleton = null;

    public static LazySingleton getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

VS 与问题

饿汉式单例模式:线程安全,在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。没有lazy loading的效果,如何一直不使用,会造成内存浪费

懒汉式单例模式:懒加载,在类加载时不初始化。但是线程不安全。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,此时,另一个线程也通过了这个判断语句,这时判断都为true,便会产生多个实例。所以在多线程环境下不可使用这种方式。

如何解决懒汉式单例模式的线程安全问题呢?

1、同步方法(不推荐)

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 * 1、效率太低,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。不推荐使用
 */
public class SyncLazySingleton {
    private SyncLazySingleton() {}

    private static SyncLazySingleton singleton;

    public static synchronized SyncLazySingleton getInstance() {
        if (singleton == null) {
            singleton = new SyncLazySingleton();
        }
        return singleton;
    }
}

2、同步代码块(warning:不能解决

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 * 1、不能解决线程安全问题,跟之前一样,假如一个线程进入了
 * if (singleton == null)判断语句块,此时,另一个线程
 * 也通过了这个判断语句,这时便会产生多个实例。
 */
public class SyncBlockLazySingleton {
    private SyncBlockLazySingleton() {
    }

    private static SyncBlockLazySingleton singleton;

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

双检索(推荐)

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 * 1、volatile的重要性,在线程技术那边说过,可以参考
 */
public class DoubleCheckSingleton {
    private DoubleCheckSingleton() {
    }

    private static volatile DoubleCheckSingleton singleton;

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

静态内部类(推荐)

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 * 1、采用类加载的机制来保证初始化实例时只有一个线程(static final)
 * 2、Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用
 * getInstance()方法,才会装载完成Singleton的实例化(Lazy Loading)。
 * 3、类的静态属性只会在第一次加载类的时候初始化,JVM帮助我们保证了线程
 * 的安全性,在类进行初始化时,别的线程是无法进入的。
 */
public class InnerStaticSingleton {
    private InnerStaticSingleton() {
    }

    private static class SingletonInstance {
        private static final InnerStaticSingleton INSTANCE = new InnerStaticSingleton();
    }

    public static InnerStaticSingleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

枚举(很强,但是好像使用的人较少)

package singleton;

/**
 * @Author fitz.bai
 * @Date 2018/8/28 10:58
 */

/**
 *
 * 自由序列化,线程安全,保证单例
 *
 * 1、enum是由class实现的,由于enum是通过继承了Enum类实现的,enum结构
 * 不能够作为子类继承其他类,但是可以用来实现接口。此外,enum类也不能够被继承,
 * 在反编译中,我们会发现该类是final的。
 *
 * 2、enum有且仅有private的构造器,防止外部的额外构造,这恰好和单例模式吻合,
 * 也为保证单例性做了一个铺垫。这里展开说下这个private构造器,如果我们不去手写
 * 构造器,则会有一个默认的空参构造器,我们也可以通过给枚举变量参量来实现类的初
 * 始化。
 *
 */
public class EnumSingleton {
    private EnumSingleton() {
    }

    public static EnumSingleton getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    private static enum Singleton {
        INSTANCE;
        private EnumSingleton singleton;
        // 省略了private,但是他并不是默认的权限,仍然是private
        Singleton() {
            singleton = new EnumSingleton();
        }

        public EnumSingleton getInstance() {
            return singleton;

        }
    }
}

单例模式在Java中的应用

Runtime,JDK API对于这个类的解释”每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接,可以通过getRuntime方法获取当前运行时。应用程序不能创建自己的Runtime类实例。”就是完美的单例模式的应用适用。

以上就是个人单例模式的理解,希望大家有问题可以提出,共同学习。

猜你喜欢

转载自blog.csdn.net/qq_22798455/article/details/82145697