题目:设计一个类,我们只生成该类的一个实例。

  • 只生成一个实例的类就是实现Singleton(单例)模式的类型。本题其实主要考察我们设计模式,因为面试的时候先来一个简单的,并且喜欢面设计模式相关的题目,而且,在常用的设计模式中,Singleton是比较简单的,而且可以通过简洁的代码来实现。所有Singleton是常见的面试题目。

以下解题思路

  • 由于只生成一个实例,我们可以将构造方法设置成私有构造,使得其他方法无法通过实例创建。我们可以定义一个静态实例,在需要的时候创建该实例,如下思路:
解法一:只适用单线程
//不好解法一
/*** @author liaojiamin* @Date:Created in 10:18 2020/10/27*/
public class Singleton {private Singleton(){}public static Singleton singleton = null;public static Singleton getInstance(){if(null == singleton){singleton = new Singleton();}return singleton;}
}
  • 分析,以上代码是在不考虑并发的情况下的简单单例模式,从下面几点来保证了得到的实例是唯一的:

    • 静态实例:带有static关键字的熟悉在每个类中都是唯一的(在class文件被加载到jvm中的准备阶段,方法区为这些类变量进行内存分配,并且进行初始化。比如被static修饰的字段。非static修饰属性会在类实例化时候在对内存中分配存储空间。因此类变了在class文件被加载的时候才有,并不受实例化的影响)
    • 私有构造方法限制客户通过实例创建
    • 提供getInstance唯一入口
  • 以上代码存在并发问题,用如下方法进行检测:
/*** @author liaojiamin* @Date:Created in 14:44 2020/10/27*/
public class TestSingletonRunnableMain {private Boolean lock;public Boolean getLock() {return lock;}public void setLock(Boolean lock) {this.lock = lock;}public static void main(String[] args) throws InterruptedException {Long startTime = System.currentTimeMillis();int num = 100;final CyclicBarrier cyclicBarrier = new CyclicBarrier(num);final Set<String> set = Collections.synchronizedSet(new HashSet<String>());ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat("nameThreadFactory-01").build();ExecutorService executorService = new ThreadPoolExecutor(100, 100, 1,TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), nameThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < num; i++) {executorService.execute(new Runnable() {@Overridepublic void run() {try {cyclicBarrier.await();Singleton  singletonThree = Singleton.getInstance();set.add(singletonThree.toString());} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}});}Thread.sleep(2000);System.out.println("in more thread get singleton");for (String s : set) {System.out.println(s);}executorService.shutdown();}
}
//输出:
//in more thread get singleton
//com.ljm.resource.math.Singleton@53fe75ec
//com.ljm.resource.math.Singleton@4eba943c
解法二:并发安全,但是效率低
/*** @author liaojiamin* @Date:Created in 14:33 2020/10/27*/
public class SingletonOne {private SingletonOne(){}public static SingletonOne singletonOne;public static SingletonOne getInstance(){synchronized (SingletonOne.class){if(null == singletonOne){singletonOne = new SingletonOne();}return singletonOne;}}
}
  • 区别在于获取对象时,用synchronized关键字进行加锁,同一时刻只能有一个线程执行,等第一个线程创建完时间后。第一个线程释放同步锁,此时第二个线程可以加上同步锁,并允许接下来的代码。这个时候,实例已经被第一个线程创建,所以第二个线程不会再重复创建实例,保证得到的是同一个实例(synchronized加锁是一个非常耗时的操作,应该尽量避免)
可行的解法:加同步锁前后两次判断
  • 我们可以优化以上方法,只在我们需要的时候镜像同步锁,如下。
/*** @author liaojiamin* @Date:Created in 14:38 2020/10/27*/
public class SingletonTwo {private SingletonTwo(){}public static SingletonTwo singletonTwo;public static SingletonTwo getInstance(){if(null == singletonTwo){synchronized (SingletonOne.class){if(null == singletonTwo){singletonTwo = new SingletonTwo();}}}return singletonTwo;}
}
  • 以上SingletonTwo中只有instance为null的时候,才需要加锁。当instance已经创建出来后,无须加锁。因为只有第一次instance为null,所以执行的结果就是只有第一次的时候才会加锁,其他时候都无需锁,所有效率高得多。
  • 两次判断的原因:
    • 假设我们去掉同步块中的是否为null的判断,有这样一种情况,假设A线程和B线程都在同步块外面判断了synchronizedSingleton为null,结果A线程首先获得了线程锁,进入了同步块,然后A线程会创造一个实例,此时synchronizedSingleton已经被赋予了实例,A线程退出同步块,直接返回了第一个创造的实例,此时B线程获得线程锁,也进入同步块,此时A线程其实已经创造好了实例,B线程正常情况应该直接返回的,但是因为同步块里没有判断是否为null,直接就是一条创建实例的语句,所以B线程也会创造一个实例返回,此时就造成创造了多个实例的情况。
推荐的解法:利用静态属性
/*** @author liaojiamin* @Date:Created in 14:40 2020/10/27*/
public class SingletonThree {private SingletonThree(){}private static SingletonThree singletonThree = new SingletonThree();public static SingletonThree getInstance(){return singletonThree;}
}
  • SingletonThree实现的方式简洁。我们初始化静态变量singletonThree 时候创建一个实例。由于Java是在类加载的时候在方法区对静态属性分配内存,并且只初始化一次,这样我们就能保证只初始化一次singletonThree 。
  • 因为SingletonThree 中singletonThree 的初始化并不是调用getInstance的时候创建的,而且在类加载的时候就已经创建,我们使用的时候调用getInstance方法他其实是不会创建新的实例,所有他会提前创建好实例,不管你之后是否需要
最优的解法:实现按需创建实例
/*** @author liaojiamin* @Date:Created in 15:45 2020/10/27*/
public class SingletonFour {private SingletonFour(){}private static class SingletonInstance{static SingletonFour singletonFour = new SingletonFour();}public static SingletonFour getInstance(){return SingletonInstance.singletonFour;}
}
  • SingletonFour 中我们定义了一个私有的内部类SingletonInstance我们利用:**内部静态类不会自动初始化,只有调用静态内部类的方法,静态域,或者构造方法的时候才会加载静态内部类。**的特点来实现的此处的单例
  • 由于静态内部类是私有的,只有我们在调用getInstance方法的时候被用到,因此当我们试图通过属性SingletonFour .getInstance得到SingletonFour 时候,会自动调用内部类SingletonInstance的静态构造方法创建实例,并初始化内部类中的静态变量 singletonFour

解法比较

  • 以上五种实现方案中,第一张方法在多线程环境中不能正常工作,第二种线程安全的方法但是实际效率低下,都不是我们所期待的可运行的解法。第三种方法中,我们通过两次判断加一次锁确保在多线程环境能高效运转。第四种利用java静态属性的特性,确保值创建一个实例。第五种方法利用私有嵌套类型的特性,做到只在真正需要的时候才创建实例,提高空间使用率。第五种解法是最优解。

下一篇: 数据结构与算法–数组:二维数组中查找

数据结构与算法--实现Singleton模式相关推荐

  1. 数据结构与算法--数组:二维数组中查找

    数组 数组最简单的是数据结构,占据一整块连续的内存并按照顺序存储数据,创建数组时候,我们需要首先指定数组的容量大小,然后根据大小分配内存.即使我们只在数组中存储一个元素,亚需要为所有数据预先分配内存, ...

  2. 数据结构:KMP算法 串的模式匹配算法(全网最详细)

    目录 KMP模式匹配算法 简述 KMP模式匹配算法原理 如果人眼来优化的话,怎样处理         接下来我们自己来发现j的移动规律: 这一段公式证明了我们为什么可以直接将j移动到k而无须再比较前面 ...

  3. 面试题库 之 数据结构与算法 100题

    1.把二元查找树转变成排序的双向链表 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表. 要求不能创建任何新的结点,只调整指针的指向. 10 / \ 6 14 / \ / \ 4 8 ...

  4. 数据结构与算法设计思路和考察点

    网上摘录,想分专题放到一起.数据结构常见的问题包括字符串方面.链表的各种操作.树的各种操作,以及各种变形和与其它数据结构的结合使用. 面试题目 字符串专题 1.将字符串转换成整数,将整数转换为字符串, ...

  5. 为什么“不懂数据结构与算法”的程序员一定走不远?

    踏上了编程之路,也就意味着你选择了一种终身学习的生活方式.每一个程序员都要练就十八般武艺,而掌握数据结构与算法就像修炼了九阳神功.换句话说,掌握了数据结构与算法,你的内功修炼速度就会有质的飞跃. &q ...

  6. 数据结构与算法-C++实现

    前沿 1.数据结构和算法的理解 答: 我们如何把现实中大量而复杂的问题,以特定的数据类型和特定的存储结构保存到主存储器(内存)中. (注:数据结构解决了数据存储的问题,比如要存储一个班级50人的成绩, ...

  7. 《数据结构与算法 C语言版》—— 3.8习题

    本节书摘来自华章出版社<数据结构与算法 C语言版>一 书中的第3章,第3.8节,作者:徐凤生,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 3.8习题 1名 ...

  8. Javascript的数据结构与算法(一)

    1数组 1.1方法列表 数组的常用方法如下: concat: 链接两个或者更多数据,并返回结果. every: 对数组中的每一项运行给定的函数,如果该函数对每一项都返回true,则返回true. fi ...

  9. 数据结构与算法笔记 - 绪论

    数据结构与算法笔记 - 绪论 1. 什么是计算 2. 评判DSA优劣的参照(直尺) 3. 度量DSA性能的尺度(刻度) 4. DSA的性能度量的方法 5. DSA性能的设计及其优化 x1. 理论模型与 ...

最新文章

  1. [BZOJ 4800][Ceoi2015]Ice Hockey World Championship(Meet-in-the-Middle)
  2. Ubuntu14.04LST安装weblogic11g
  3. jboss4。0下mysql数据源的配置
  4. EntityFramework的安装
  5. mysql5.7 innodb myisam 区别_InnoDB与MyISAM的区别(高性能MySQL笔记)
  6. 查询数据库中所有表名称
  7. 开发环境配置--Ubuntu+Qt4+OpenCV(二)
  8. Bossie Awards 开源大数据工具最佳列表
  9. 学维修电脑要多久_学古筝难吗?古筝要多久才能学会?
  10. MFC学习--文本框
  11. 如何快速实现告警短信的通知方式?
  12. 【百度网盘】 个人资源共享
  13. 中学计算机教师人生职业规划,初中教师个人职业发展规划
  14. VOLTE学习笔记(一)——VOLTE网络结构
  15. python创建目录(文件夹)
  16. LitJson在Unity中的使用
  17. dlink平台上面flink-connect-kudu开发
  18. 定位人工智能时代的“拍照机器人”,美图M8为何能撂倒Angelababy等明星
  19. 欣瑞达一分钟教程,如何接RS232/RS485线
  20. Python 绘制3D宇宙飞船

热门文章

  1. Android安全与逆向之在ubuntu上面搭建NDK环境
  2. linux如何获取网卡计数信息,Linux下如何获取网卡信息
  3. go获取项目内所有proto_gRPC学习之三:初试GO版gRPC开发
  4. 太牛了,原来古人是这样铸造钱币的。。。
  5. 你真的知道Java同步锁何时释放?
  6. 初中数学知识点总结_初中数学知识点总结大全_经典版_
  7. python echo函数_python如何调用php文件中的函数详解
  8. vs code python 插件_工具篇-vscode效率提升插件
  9. 模拟时钟中断的产生及设计一个对时钟中断事件进行处理的模拟程序_操作系统基础6-支持操作系统的最基本的硬件-中断...
  10. 通过python实现linux切换用户_Python操作远程服务器切换到root用户