单例模式实现延迟加载在多线程下的思考(双检锁和内部类)
来源:互联网 发布:新网域名如何解析 编辑:程序博客网 时间:2024/06/05 15:14
本文参考了《java并发编程艺术》一书中的部分内容,增加了部分自己的理解。
1、单例模式最简单的实现:
- class Single{
- public static Single single = new Single();
- private Single(){}
- public static getInstance(){
- return single ;
- }
- }
2、通过synchronized实现延迟加载
- class LazySingle{
- private static LazySingle single = null
- private LazySingle(){}
- public static synchronized getInstance(){
- if(single == null ){
- single = new LazySingle();
- }
- return single ;
- }
- }
此方法存在性能问题,多线程调用getInstance()方法,都需要检查锁是否存在;
3、通过双检锁实现
- class LazySingle{
- private static LazySingle single = null
- private LazySingle(){}
- public static getInstance(){
- if(single == null ){
- synchronized(LazySingle.class){
- if(single == null){
- single = new LazySingle();
- }
- }
- }
- return single ;
- }
- }
当线程执行到第5行时,代码读取到single不为空时,single引用的对象可能还没有完成初始化。
我们对第8行代码进行分析(single = new LazySingle()), 这行代码在编译器中会被翻译成如下伪代码:- memory = allocate(); //分配对象的内存空间
- ctorInstance(memory); //初始化对象
- instance = memory; //设置instance指向刚分配的内存地址
上面第2行和3行代码可能会被重排序(为什么会重排序,大家可以参考《java并发编程艺术》一书),重排序的结果为
- memory = allocate(); //分配对象的内存空间
- instance = memory; //设置instance指向刚分配的内存地址
- ctorInstance(memory); //初始化对象
此时,如果2行执行结束,刚好有个线程访问到双检索代码的第5行时,single并不为空,但是初始化还未完成,将会返回一个空的引用。
解决这个问题的方法有两种。
- class LazySingle{
- private volatile static LazySingle single = null
- private LazySingle(){}
- public static getInstance(){
- if(single == null ){
- synchronized(LazySingle.class){
- if(single == null){
- single = new LazySingle();
- }
- }
- }
- return single ;
- }
- }
5、内部类方式解决
初始化一个类包括执行这个类的静态初始化和初始化在这个类中声明的静态字段。
因此当在单例类中定义 public static Single single = new Single(); 的变量时,不具备延迟加载的特性,JVM在load到这个类时就就行了变量的初始化。
根据java语言规范,在首次发生如下任意情况时,一个类或者接口类型T将被立即初始化:
- T是一个类,而且一个T类型的实例被创建;
- T是一个类,且T中声明的一个静态方法被调用;
- T中声明的一个静态字段被赋值;
- T中声明的一个静态字段被使用,而且这个字段不是一个常量字段;
- T是一个顶级类,而且一个断言语句嵌套在T内部被执行;
public class StaticSingle {
private StaticSingle(){}private static class StaticSingleHolder{private static StaticSingle instance = new StaticSingle();}public static StaticSingle getInstance(){return StaticSingleHolder.instance;}
}
使用内部类的方式实现单例,既可以实现延迟加载,也不必使用同步关键字,是一种比较完善的实现
0 1
- 单例模式实现延迟加载在多线程下的思考(双检锁和内部类)
- 使用静态内部类实现延迟加载单例模式
- 单例模式中采用的延迟加载创建单例(内部类)
- java实现多线程延迟加载的单例模式
- 【J2SE】为什么静态内部类的单例可以实现延迟加载
- 内部类实现多线程环境中的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 多线程下安全的单例模式(双重校验锁实现,登记者模式实现,利用内部类实现,利用枚举实现)
- Java多线程编程环境中单例模式的实现 (内部类实现多线程环境中的单例模式)
- Java多线程编程环境中单例模式的实现 (内部类实现多线程环境中的单例模式)
- 内部类实现单例模式
- 静态内部类实现单例模式
- 单例模式-静态内部类的实现(线程安全)
- 单例模式的另一种实现(内部类)
- 读书码代码
- HDU 1024 Max Sum Plus Plus(DP)
- <RP>网页设计中的默认字体:{font: 12px/1.5 Tahoma, Helvetica, Arial, sans-serif;}
- c++类成员的初始化
- java io 流解析
- 单例模式实现延迟加载在多线程下的思考(双检锁和内部类)
- HDU1052
- Linux下实现秒级定时任务的两种方案(crontab 每秒运行)
- !!!IP地址转换
- Autodesk Vred 2016
- 15电气郄慧敏vb作业3红糖水白糖水的交换
- Fragment类
- Codeforces 451 E Devu and Flowers
- 买卖股票收益最大问题