模式说明
某些场景要求一个对象只能有一个实例,可以通过单例模式实现。由于只能有一个实例,因此该类的构造器必须用private修饰来禁止通过new操作符来新建实例。类内持有一个private修饰的本类类型的实例,又因为只能有一个实例,所以使其为类成员,如该类类名Singleton,可声明为:private static Singleton instance;
通过类内的一个public修饰的getIntance方法来获取实例,该方法也是static的。根据实例产生的时机分为饿汉模式和懒汉模式,懒汉模式中有线程安全和不安全的写法。
饿汉模式
类属性Singleton instance在声明时就实例化。private static Singleton instance = new Singleton();
懒汉模式
类属性Singleton只声明但不实例化,将实例化动作放到getInstance方法中,则只有调用getInstance时才能获取Singleton的实例。懒汉模式因为在调用getInstance时才获得实例,存在多线程竞争的问题,因此可以结合volatile(避免指令重排)和synchronized(保证原子性)以双锁检测方式将Singleton写成线程安全的类。
本示例演示饿汉写法,非线程安全懒汉写法和双锁检测线程安全写法。
结构
单例类
持有一个本类的类属性,维护一个getInstance方法。
代码演示
package com.yukiyama.pattern.creation;/*** 单例模式*/public class SingletonDemo {public static void main(String[] args) {SingletonSimple s1 = SingletonSimple.getInstance();SingletonSimple s2 = SingletonSimple.getInstance();// 输出“true”,说明s1与s2是同一个实例System.out.println(s1 == s2);SingletonHungry s3 = SingletonHungry.getInstance();SingletonHungry s4 = SingletonHungry.getInstance();// 输出“true”,说明s3与s4是同一个实例System.out.println(s3 == s4);Singleton s5 = Singleton.getInstance();Singleton s6 = Singleton.getInstance();// 输出“true”,说明s3与s4是同一个实例System.out.println(s5 == s6);}}/*** 饿汉模式* 在类加载时创建常量化实例,不存在多线程导致可能出现多个实例的问题*/class SingletonHungry{// 在定义SingletonHungry类型的属性时直接实例化,类内可以访问private构造器private static final SingletonHungry instance = new SingletonHungry();// 将构造器声明为private,外部无法用new获取private SingletonHungry() {}// 外部通过一个public的getInstance()方法获取该类实例public static SingletonHungry getInstance() {return instance;}}/*** 多线程下的双锁检测(Double-Check Locking)单例* 懒汉模式*/class Singleton{// 以volatile修饰private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {// 第一次判断的目的是避免每次getInstance()都加锁// 若已经存在实例,直接返回if(instance == null) {synchronized (Singleton.class) {// 再次判断是防止两个线程在instance==null时// 同时进入第一个if内,由于加锁,其中一个先new了// 实例,此时必须再判断一次防止第二个也new一个实例if(instance == null) {instance = new Singleton();}}}return instance;}}/*** 懒汉模式* 非线程安全版*/class SingletonSimple {private static SingletonSimple instance;// 将构造器声明为private,外部无法用new获取private SingletonSimple() {}// 外部通过一个public的getInstance()方法获取该类实例public static SingletonSimple getInstance() {// 每次获取前判断该类实例是否已存在,若无则new一个if (instance != null) {instance = new SingletonSimple();}return instance;}}
