设计模式——单例模式

来源:互联网 发布:js递归树形数据结构 编辑:程序博客网 时间:2024/06/04 19:30

上次面试也被问到了单例模式,当时回答的比较粗糙,今天刚好在看设计模式,自己写一点总结当做笔记。

单例模式:顾名思义,就是某一个类只实例化一次,不允许实例化多个;单例类自己创建一个实例,供全局使用。


那么如何才能保证一个类自己实例化的的对象可以供全局使用,而不会再次被实例化创建新的对象呢?

我们可以通过限制构造函数的方法来限制单例类的实例化,实例化一个类一般是调用其构造函数进行创建实例对象,只需要将构造函数设计为 private ,那么其他类无法调用 该单例类的构造函数。

简单的实现:

public class Singleton1 {private static Singleton1 instance = null;private Singleton1(){}public static Singleton1 getInstance(){if(instance == null)instance = new Singleton1();return instance;}}

当然了,单例模式主要是应用在多线程的场景下,所以我们需要实现获取单例对象的同步处理,否则可能创建多个对象。

典型的单例类实现线程同步是在getInstance 方法前面加上关键字 sychronized 的访问控制:


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

但是这样代价比较高,每次访问单例类时都要先获取这个进程锁,但实际上我们只需要在创建时才使用 锁 来避免创建重复对象。

如何提高效率呢?可以使用目前最好的方法双重检测锁(也叫 双重同步锁):先看代码:


public static Singleton1 getInstance(){//第一次检测,先判断是否已创建if(instance == null){//如果还没有创建则获取线程锁synchronized(Singleton1.class){//第二次判断,检测是否已经创建if(instance == null)instance = new Singleton1();}}return instance;}

如果没有第二次检测,无法保证只实例化一个对象。假设现在有两个线程分别为 A 和 B :

A 线程进行第一次判断发现 instance 为 null,说明还没有创建,那么程序继续往下,线程A将获得线程锁。但是假如在这个时候,线程 B 也检测到 instance 为 null (因为还没创建),那么线程B也需要获取线程锁以完成后面的操作。我们可以假如 A 先获得线程锁那么 B 则要等待线程 A  释放线程锁,当 A 释放线程锁时,B 获取线程锁。这个时候 线程 A 已经创建了一个单例类的实例对象,但是线程 B 并不知道已经创建了。因为在线程 B进行第一次检测时 instance 为 null,所以我们需要第二次判断检测,此时检测到 instance 不为 null 所以返回 instance 对象 。如果没有第二次判断那么 线程 B 也将进行一次对象的创建,这样就违背了单例模式的准则。







原创粉丝点击