文档章节

java singleton模式四种线程安全的实现

子群
 子群
发布于 2016/12/09 14:48
字数 1227
阅读 16
收藏 2
点赞 0
评论 0

1.描述:

    Singleton(单例)是设计模式的一种,为了保证一个类仅有一个实例,并提供一个访问它的全局访问点。

2.主要特点:     

    1)单例类确保自己只有一个实例(构造函数私有:不被外部实例化,也不被继承)。

    2)单例类必须自己创建自己的实例。

    3)单例类必须为其他对象提供唯一的实例。

3.单例模式的应用:

    资源管理器,回收站,打印机资源,线程池,缓存,配置信息类,管理类,控制类,门面类,代理类通常被设计为单例类

    如果程序有多个类加载器又同时使用单例模式就有可能多个单例并存就要找相应解决方法了

4.实现方法:

如果应用程序总是创建并使用单例实例或在创建和运行时开销不大。

1).Eager initialization 饿汉式单例类(依赖jvm在加载类时创建唯一单例实例)

public class EagerSingleton {  
        // jvm保证在任何线程访问uniqueInstance静态变量之前一定先创建了此实例  
        private static EagerSingleton uniqueInstance = new EagerSingleton();  
  
        // 私有的默认构造子,保证外界无法直接实例化  
        private EagerSingleton() {  
        }  
  
        // 提供全局访问点获取唯一的实例  
        public static EagerSingleton getInstance() {  
                return uniqueInstance;  
        }  
}

如果开销比较大,希望用到时才创建就要考虑延迟实例化,或者Singleton的初始化需要某些外部资源(比如网络或存储设备),就要用后面的方法了.

 

2)Lazy initialization 懒汉式单例类

public class LazySingleton {  
        private static LazySingleton uniqueInstance;  
  
        private LazySingleton() {  
        }  
  
        public static synchronized LazySingleton getInstance() {  
                if (uniqueInstance == null)  
                        uniqueInstance = new LazySingleton();  
                return uniqueInstance;  
        }  
} 

同步一个方法可能造成程序执行效率下降100倍,完全没有必要每次调用getInstance都加锁,事实上我们只想保证一次初始化成功,其余的快速返回而已,如果在getInstance频繁使用的地方就要考虑重新优化了.

 

3)"双检锁"(Double-Checked Lock)尽量将"加锁"推迟,只在需要时"加锁"(仅适用于Java 5.0 以上版本,volatile保证原子操作) 
happens-before:"什么什么一定在什么什么之前运行",也就是保证顺序性.
现在的CPU有乱序执行的能力(也就是指令会乱序或并行运行,可以不按我们写代码的顺序执行内存的存取过程),并且多个CPU之间的缓存也不保证实时同步,只有上面的happens-before所规定的情况下才保证顺序性.

JVM能够根据CPU的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能.

如果没有volatile修饰符则可能出现一个线程t1的B操作和另一线程t2的C操作之间对instance的读写没有happens-before,可能会造成的现象是t1的B操作还没有完全构造成功,但t2的C已经看到instance为非空,这样t2就直接返回了未完全构造的instance的引用,t2想对instance进行操作就会出问题.

    volatile 的功能:
1. 避免编译器将变量缓存在寄存器里  
2. 避免编译器调整代码执行的顺序

优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

public class DoubleCheckedLockingSingleton {  
        // java中使用双重检查锁定机制,由于Java编译器和JIT的优化的原因系统无法保证我们期望的执行次序。  
        // 在java5.0修改了内存模型,使用volatile声明的变量可以强制屏蔽编译器和JIT的优化工作  
        private volatile static DoubleCheckedLockingSingleton uniqueInstance;  
  
        private DoubleCheckedLockingSingleton() {  
        }  
  
        public static DoubleCheckedLockingSingleton getInstance() {  
                if (uniqueInstance == null) {  
                        synchronized (DoubleCheckedLockingSingleton.class) {  
                                if (uniqueInstance == null) {  
                                        uniqueInstance = new DoubleCheckedLockingSingleton();  
                                }  
                        }  
                }  
                return uniqueInstance;  
        }  
}  

4)Lazy initialization holder class 满足所有 Double-Checked Locking 满足的条件,并且没有显示的同步操作

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

根据jvm规范,当某对象第一次调用LazyInitHolderSingleton.getInstance()时,LazyInitHolderSingleton类被首次主动使用,jvm对其进行初始化(此时并不会调用LazyInitHolderSingleton()构造方法),然后LazyInitHolderSingleton调用getInstance()方法,该方法中,又首次主动使用了SingletonHolder类,所以要对SingletonHolder类进行初始化,初始化中,INSTANCE常量被赋值时才调用了 LazyInitHolderSingleton的构造方法LazyInitHolderSingleton(),完成了实例化并返回该实例。

当再有对象(也许是在别的线程中)再次调用LazyInitHolderSingleton.getInstance()时,因为已经初始化过了,不会再进行初始化步骤,所以直接返回INSTANCE常量即同一个LazyInitHolderSingleton实例。

© 著作权归作者所有

共有 人打赏支持
子群
粉丝 7
博文 30
码字总数 35720
作品 0
深圳
程序员
java面试必备之ThreadLocal

按照传统的经验,如果某个对象是非线程安全的,在多线程环境下对象的访问需要采用synchronized进行同步。但是模板类并未采用线程同步机制,因为线程同步会降低系统的并发性能,此外代码同步解...

编程老司机 ⋅ 05/16 ⋅ 0

为什么我墙裂建议大家使用枚举来实现单例

我们知道,单例模式,一般有七种写法,那么这七种写法中,最好的是哪一种呢?为什么呢?本文就来抽丝剥茧一下。 哪种写单例的方式最好 在StakcOverflow中,有一个关于What is an efficient ...

冷_6986 ⋅ 06/13 ⋅ 0

为什么我墙裂建议大家使用枚举来实现单例。

关于单例模式,我的博客中有很多文章介绍过。作为23种设计模式中最为常用的设计模式,单例模式并没有想象的那么简单。因为在设计单例的时候要考虑很多问题,比如线程安全问题、序列化对单例的...

⋅ 06/10 ⋅ 0

java开发中的常用的设计模式

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代...

qq_38024548 ⋅ 05/28 ⋅ 0

2018年Java编程学习面试最全知识点总结

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 05/14 ⋅ 0

备战一线互联网公司Java工程师面试题 (1)

Java重点知识 多线程(线程状态、线程并发,Synchronized与Lock的区别和底层原理,常用的锁及其使用场景和原理, volatile和ThreadLocal解决了什么问题,CAS在Java中的实现 线程池原理和实现...

j4love ⋅ 04/14 ⋅ 0

ThreadLocal趣谈 —— 杨过和他的四个冤家

一个一个上 一日醒来,杨过发现小龙女离家出走,于是外出寻找,不料碰上了金轮法王、李莫愁、裘千尺、公孙止四个冤家。 “哼,四个打我一个,算什么英雄好汉,有本事的,一个一个上!” 按照...

SexyCode ⋅ 06/12 ⋅ 0

Java并发编程之ThreadLocal源码分析

多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的,多线程访问同一个共享变量特别容易出现并发问题,特别是多个线程需要对一个...

狂小白 ⋅ 06/03 ⋅ 0

Java 使用 happen-before 规则实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。按照官方的...

stateIs0 ⋅ 01/20 ⋅ 0

JAVA程序员面试题整理(较全面)

以下是在面试中可能会遇到的问题,话不多说,往下看 1、面向对象的特征有哪些方面? 2、访问修饰符public,private,protected,以及不写(默认)时的区别? 3、String 是最基本的数据类型吗? ...

编程大侠 ⋅ 04/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Sqoop

1.Sqoop: 《=》 SQL to Hadoop 背景 1)场景:数据在RDBMS中,我们如何使用Hive或者Hadoop来进行数据分析呢? 1) RDBMS ==> Hadoop(广义) 2) Hadoop ==> RDBMS 2)原来可以通过MapReduce I...

GordonNemo ⋅ 37分钟前 ⋅ 0

全量构建和增量构建的区别

1.全量构建每次更新时都需要更新整个数据集,增量构建只对需要更新的时间范围进行更新,所以计算量会较小。 2.全量构建查询时不需要合并不同Segment,增量构建查询时需要合并不同Segment的结...

无精疯 ⋅ 47分钟前 ⋅ 0

如何将S/4HANA系统存储的图片文件用Java程序保存到本地

我在S/4HANA的事务码MM02里为Material维护图片文件作为附件: 通过如下简单的ABAP代码即可将图片文件的二进制内容读取出来: REPORT zgos_api.DATA ls_appl_object TYPE gos_s_obj.DA...

JerryWang_SAP ⋅ 今天 ⋅ 0

云计算的选择悖论如何对待?

导读 人们都希望在工作和生活中有所选择。但心理学家的调查研究表明,在多种选项中进行选择并不一定会使人们更快乐,甚至不会产生更好的决策。心理学家Barry Schwartz称之为“选择悖论”。云...

问题终结者 ⋅ 今天 ⋅ 0

637. Average of Levels in Binary Tree - LeetCode

Question 637. Average of Levels in Binary Tree Solution 思路:定义一个map,层数作为key,value保存每层的元素个数和所有元素的和,遍历这个树,把map里面填值,遍历结束后,再遍历这个map,把每...

yysue ⋅ 今天 ⋅ 0

IDEA配置和使用

版本控制 svn IDEA版本控制工具不能使用 VCS-->Enable Version Control Integration File-->Settings-->Plugins 搜索Subversion,勾选SVN和Git插件 删除.idea文件夹重新生成项目 安装SVN客户......

bithup ⋅ 今天 ⋅ 0

PE格式第三讲扩展,VA,RVA,FA的概念

作者:IBinary 出处:http://www.cnblogs.com/iBinary/ 版权所有,欢迎保留原文链接进行转载:) 一丶VA概念 VA (virtual Address) 虚拟地址的意思 ,比如随便打开一个PE,找下它的虚拟地址 这边...

simpower ⋅ 今天 ⋅ 0

180623-SpringBoot之logback配置文件

SpringBoot配置logback 项目的日志配置属于比较常见的case了,之前接触和使用的都是Spring结合xml的方式,引入几个依赖,然后写个 logback.xml 配置文件即可,那么在SpringBoot中可以怎么做?...

小灰灰Blog ⋅ 今天 ⋅ 0

冒泡排序

原理:比较两个相邻的元素,将值大的元素交换至右端。 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第...

人觉非常君 ⋅ 今天 ⋅ 0

Vagrant setup

安装软件 brew cask install virtualboxbrew cask install vagrant 创建project mkdir -p mst/vmcd mst/vmvagrant init hashicorp/precise64vagrant up hashicorp/precise64是一个box......

遥借东风 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部