java 单例类对象锁_Java安全发布对象(单例)的几种方式

基础知识

类的初始化

JVM在类被加载后,并且被线程使用之前,会进行类的初始化。在初始化期间,JVM将会获取一个锁,以同步多个线程对类的初始化。

根据Java语言规范,在首次发生下列任意一种情况时,一个类或接口类型T将被立即初始化:

T是一个类,而且一个T类型的实例被创建。

T是一个类,且T中声明的一个静态方法被调用。

T中声明的一个静态字段被赋值。

T中声明的一个静态字段被使用,而且这个字段不是一个常量字段。

T是一个顶级类(Top Level Class,见Java语言规范的§7.6),而且一个断言语句嵌套在T内部被执行。

volatile

volatile可以理解为是轻量级的synchronized。

保证共享变量的“可见性”,当一个线程修改了一个共享变量时,另一个线程能读到这个修改的值。

如果一个变量声明为volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。

提前初始化

提前初始化,是指在类初始化期间对资源进行初始化,即在线程调用之前已完成好初始化。

public class EagerInitialization {

private static Resource resource = new Resource();

public static Resource getResource() {

return resource;

}

static class Resource {

}

}

复制代码

该方案的优点是不需要每次调用都进行同步,并且避免第1次调用时初始化的开销。

延迟初始化

有时候,我们需要推迟一些高开销的对象初始化操作,并且只有当使用这些对象时才进行初始化。

下面是常见的三种延迟初始化方案。

基于同步

public class SafeLazyInitialization {

private static Resource resource;

public synchronized static Resource getInstance() {

if (resource == null)

resource = new Resource();

return resource;

}

static class Resource {

}

}

复制代码

该方案由于使用了synchronized,在多线程频繁调用的情况下,将会导致程序执行性能的下降。因此,该方案通常很少使用。

基于类初始化

利用类初始化的安全性,我们可以在无锁的情况下,实现资源的安全初始化。

public class ResourceFactory {

private static class ResourceHolder {

public static Resource resource = new Resource();

}

public static Resource getResource() {

return ResourceHolder.resource;

}

static class Resource {

}

}

复制代码

其中,return ResourceHolder.resource将促使ResourceHolder进行初始化,从而初始化resource = new Resource()。

双重检测锁

public class SafeDoubleCheckedLocking {

private volatile static Instance instance; // 这里必须使用volatile

public static Instance getInstance() {

if (instance == null) {

synchronized (SafeDoubleCheckedLocking.class) {

if (instance == null)

instance = new Instance(); // 这里可能会出现重排序

}

}

return instance;

}

static class Instance {

}

}

复制代码

其中,instance = new Instance();可简单的分解为3步:

分配对象内存空间

初始化对象

设置instance指向对象的内存地址

特别需要注意的是,如果instance变量不声明为volatile,那么由于重排序,分解后的3步可能是:

分配对象内存空间

设置instance指向对象的内存地址

初始化对象

第2和第3步出现了重排序,使得其他线程可能在看到instance不为null的时候,事实上instance还没初始化完成。

因此,对于双重检测锁,需要通过声明volatile来防止初始化对象时的重排序。(这个解决方案需要JDK5或更高版本的支持,因为volatile的语义得到了增强)

对于延迟初始化,由于基于类初始化的方案高效且易于理解,通常来说更加推荐使用该方案。

题图:www.journaldev.com

参考:

《Java并发编程的艺术》

《Java并发编程实战》

关于我

公众号:二进制之路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值