设计模式(五):单件模式(单例模式)


一、『单件模式』定义

确保一个类只有一个实例,并提供一个全局访问点。

单件模式即单例模式。

使用场景很多,比如多个程序都要使用一个配置文件中的数据,而且要实现数据共享和交换。必须要将多个数据封装到一个对象中,而且多个程序操作的是同一个对象,也就是说必须保证这个配置文件对象的唯一性。

有两种方式实现:饿汉式和懒汉式。


二、使用

2.1 饿汉式

单件模式的急切实例化方式,上来就创建对象:

class Single {
    //2,创建一个本类对象。
    private static Single uniqueInstance = new Single();

    //1,私有化构造函数。禁止程序创建该类的对象。
    private Single() {
    }

    //3,定义一个方法返回这个对象。该方法加上public,权限最大。
    public static Single getInstance() {
        return uniqueInstance;
    }
}

/**
 * 演示
 */
public class Demo {
    public static void main(String[] args) {
        // 调用getInstance方法获取Single的对象。
        Single ss1 = Single.getInstance();
        Single ss2 = Single.getInstance();
        // ss1和ss2操作的是同个对象,因为地址相同
        System.out.println(ss1 == ss2);
    }
}

如果创建的这个单件对象一直没用到过,而且该对象也一直比较占用空间,那么饿汉式并不是很完美的解决方法。

2.2 懒汉式

单例的延迟实例化方式,用到才创建对象:

class Single {
    //2,创建一个本类对象。
    private static Single uniqueInstance = null;

    //1,私有化构造函数。禁止程序创建该类的对象。
    private Single() {
    }

    //3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
    public static Single getInstance() {
        if (uniqueInstance == null)
            uniqueInstance = new Single();
        return uniqueInstance;
    }
}

/**
 * 演示
 */
public class Demo {
    public static void main(String[] args) {
        // 调用getInstance方法获取Single的对象。
        Single ss1 = Single.getInstance();
        Single ss2 = Single.getInstance();
        // ss1和ss2操作的是同个对象,因为地址相同
        System.out.println(ss1 == ss2);
    }
}

2.3 小细节

(1)私有化构造函数是为了禁止其它程序创建该类的对象,保证只有该类内部才能创建该类的对象。

(2)想获取Single类的对象就需要调用getInstance方法。既然无法通过对象调用,那么只能用类名调用,所以getInstance方法必须是static。

(3)创建好对象后,因为uniqueInstance对象是静态的,所以通过Single.uniqueInstance这种方式是可以获取对象的,但不推荐,用方法访问是为了对对象可控,因此对象需加上private。


三、单件模式的多线程安全问题

对比饿汉式和懒汉式两种单件模式的实现,饿汉式没有多线程安全问题,但是懒汉式是有多线程安全问题的。

3.1 懒汉式分析一

初始的饿汉式实现:

class Single {
    //2,创建一个本类对象。
    private static Single uniqueInstance = null;

    //1,私有化构造函数。禁止程序创建该类的对象。
    private Single() {
    }

    //3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
    public static Single getInstance() {
        if (uniqueInstance == null)
            uniqueInstance = new Single();
        return uniqueInstance;
    }
}

问题:假设现在uniqueInstance对象还是null,然后有多个线程同时调用getInstance()方法想获得uniqueInstance对象,可能会出现多个线程都判断uniqueInstance为空,接着就重复new了对象,这里就出现多线程安全问题了。

3.2 懒汉式分析二

上面分析发现:并发访问会有安全隐患,所以加入同步机制解决安全问题。

class Single {
    //2,创建一个本类对象。
    private static Single uniqueInstance = null;

    //1,私有化构造函数。禁止程序创建该类的对象。
    private Single() {
    }

    //3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
    public static Single getInstance() {
        synchronized (Single.class) {
            if (uniqueInstance == null)
                uniqueInstance = new Single();
        }
        return uniqueInstance;
    }
}

问题:只有第一次执行此方法时,才真正需要同步。换句话说,一旦创建好uniqueInstance对象,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。

3.3 懒汉式分析三

上面分析发现:同步的出现降低了效率。可以通过双重判断的方式,解决效率问题,减少判断锁的次数。

class Single {
    //2,创建一个本类对象。
    private volatile static Single uniqueInstance = null;

    //1,私有化构造函数。禁止程序创建该类的对象。
    private Single() {
    }

    //3,定义一个方法返回这个对象。该方法是关键,加上public,权限最大。
    public static Single getInstance() {
        if(uniqueInstance == null) {
            synchronized (Single.class) {
                if (uniqueInstance == null)
                    uniqueInstance = new Single();
            }
        }
        return uniqueInstance;
    }
}

volatile关键词确保:uniqueInstance变量被初始化成Single实例时,多个线程正确地处理uniqueInstance变量。


参考

《HeadFirst设计模式》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值