单例,大家肯定都不陌生,这是Java中很重要的一个设计模式。稍微了解一点单例的朋友也都知道实现单例是要考虑并发问题的,一般情况下,我们都会使用synchronized来保证线程安全。

那么,如果有这样一道面试题:不使用synchronized和lock,如何实现一个线程安全的单例?你该如何回答?

C类应聘者:可以使用饿汉模式实现单例。如:

public class Singleton { 
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

还有部分程序员可以想到饿汉的变种:

public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}

使用static来定义静态成员变量或静态代码,借助Class的类加载机制实现线程安全单例。

面试官:除了这种以外,还有其他方式吗?

B类应聘者:

除了以上两种方式,还有一种办法,就是通过静态内部类来实现,代码如下:

public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

这种方式相比前面两种有所优化,就是使用了lazy-loading。Singleton类被装载了,但是instance并没有立即初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。

面试官:除了这种以外,还有其他方式吗?

A类应聘者:

除了以上方式,还可以使用枚举的方式,如:

public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒。

面试官:以上几种答案,其实现原理都是利用借助了类加载的时候初始化单例。即借助了ClassLoader的线程安全机制。

所谓ClassLoader的线程安全机制,就是ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。也正是因为这样, 除非被重写,这个方法默认在整个装载过程中都是同步的,也就是保证了线程安全。

所以,以上各种方法,虽然并没有显示的使用synchronized,但是还是其底层实现原理还是用到了synchronized。
面试官:除了这种以外,还有其他方式吗?
A类应聘者:

还可以使用Java并发包中的Lock实现

面试官:本质上还是在使用锁,不使用锁的话,有办法实现线程安全的单例吗?
A+类面试者:
有的,那就是使用CAS。

CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。实现单例的方式如下:

public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

private Singleton() {}

public static Singleton getInstance() {
for (;;) {
Singleton singleton = INSTANCE.get();
if (null != singleton) {
return singleton;
}

singleton = new Singleton();
if (INSTANCE.compareAndSet(null, singleton)) {
return singleton;
}
}
}
}

面试官:这种方式实现的单例有啥优缺点吗?

A++类面试者:

用CAS的好处在于不需要使用传统的锁机制来保证线程安全,CAS是一种基于忙等待的算法,依赖底层硬件的实现,相对于锁它没有线程切换和阻塞的额外消耗,可以支持较大的并行度。

CAS的一个重要缺点在于如果忙等待一直执行不成功(一直在死循环中),会对CPU造成较大的执行开销。

另外,如果N个线程同时执行到singleton = new Singleton();的时候,会有大量对象创建,很可能导致内存溢出。

不使用synchronized和lock,如何实现一个线程安全的单例的更多相关文章

  1. 设计一个线程安全的单例(Singleton)模式

    在设计单例模式的时候.尽管非常easy设计出符合单例模式原则的类类型,可是考虑到垃圾回收机制以及线程安全性.须要我们思考的很多其它.有些设计尽管能够勉强满足项目要求,可是在进行多线程设计的时候.不考虑 ...

  2. 从一个简单的Java单例示例谈谈并发

    一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写 public class UnsafeLazyInitiallization { private static Un ...

  3. 从一个简单的Java单例示例谈谈并发 JMM JUC

    原文: http://www.open-open.com/lib/view/open1462871898428.html 一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这 ...

  4. 写一个安全的Java单例

    单例模式可能是我们平常工作中最常用的一种设计模式了.单例模式解决的问题也很常见,即如何创建一个唯一的对象.但想安全的创建它其实并不容易,还需要一些思考和对JVM的了解. 1.首先,课本上告诉我,单例这 ...

  5. 报错:在做往下拉选里面拼接数据的时候 3个下拉选显示一个值 原因 @scope(单例)或者没配默认单例

    解决 @scope(多例)  这是因为造成线程并发访问不安全

  6. 【Java】synchronized与lock的区别

    从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...

  7. java多线程(3)---synchronized、Lock

    synchronized.Lock 一.概述 1.出现线程不安全的原因是什么? 如果我们创建的多个线程,存在着共享数据,那么就有可能出现线程的安全问题:当其中一个线程操作共享数据时,还未操作完成,另外 ...

  8. 解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度-美团)

    还有其他的锁,如果想要了解,参考:JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁, 用synchronized实现ReentrantLock 美团面试题参考:使用synchronized ...

  9. 多线程-synchronized、lock

    1.什么时候会出现线程安全问题? 在多线程编程中,可能出现多个线程同时访问同一个资源,可以是:变量.对象.文件.数据库表等.此时就存在一个问题: 每个线程执行过程是不可控的,可能导致最终结果与实际期望 ...

随机推荐

  1. 【maven】IDEA工程右边的maven配置中Plugins有重复的命令

    问题 解决 换一个IDEA的版本,比如2020.02 参考链接 https://ask.csdn.net/questions/1060938 https://bbs.csdn.net/topics/3 ...

  2. Unittest框架之测试套件:TestSuite

    前言 使用了unittest.main()方法执行当前模块里的测试用例. 除此之外,Unittest还可以通过测试套件构造测试用例集,再执行测试用例 将测试用例添加至TestSuite(测试套件) 方 ...

  3. 那些好用的 VS Code 插件,究竟是如何提高编码效率的?

    在上一篇文章中我们已经对 vscode 插件有了一个初步的认识与了解了,接下去我们就要"揭秘"一下市面上那些好用的 vscode 插件究竟是如何帮我们提高工作效率的. 本文首发于「 ...

  4. 异步阻塞,Manager模块,线程

    一.异步阻塞 1.并没有按照执行顺序等待结果 2.而是所有的任务都在异步执行着 3.但是我要的结果又不知道谁的结果先来,谁先结束我就先取谁的结果 很明显的异步,大家都相互执行着(异步过程),谁先结束我 ...

  5. 老vue项目webpack3升级到webpack5全过程记录(一)

    背景 19年新建的vue项目,使用的是webpack3,随着项目的积累,组件的增多导致本地构建,线上打包等操作速度极慢,非常影响开发效率和部署效率,基于此问题,本次对webpack及相关插件进行了优化 ...

  6. memcache 和 redis 的区别

    1)Redis中,并不是所有的数据都一直存储在内存中的,这是和Memcache相比一个最大的区别.2)Redis在很多方面具备数据库的特征,或者说就是一个数据库系统,而Memcache只是简单的K/V ...

  7. WPF 使用附加属性声明 ICommand

    一.ListBox中为什么选择同一项不能每次都触发SelectionChanged事件呢? ​ 当我需要每次点击ListBox的选中项,都触发事件.找到最符合的事件为SelectionChanged事 ...

  8. 网速测试利器-iperf3

    网速测试利器-iperf3 使用工具   简介 iperf3是一个网络速度测试工具,支持IPv4与IPv6,支持TCP.UDP.SCTP传输协议,可在Windows.Mac OS X.Linux.Fr ...

  9. Linux性能分析利器——火焰图的安装使用

    火焰图(flame graph)是性能分析的利器,通过它可以快速定位性能瓶颈点. perf 命令(performance 的缩写)是 Linux 系统原生提供的性能分析工具,会返回 CPU 正在执行的 ...

  10. 855 gpu强 730 3倍

    骁龙730G的GPU规模只有骁龙835的GPU规模的一半,Adreno 618是128 ALUs,而Adreno 540是256 ALUs. 根据GFXBench的数据,对GPU负载比较大的曼哈顿3. ...