双重检查锁定和延迟初始化

域名2025-11-05 13:03:317

双重检查锁定的双重锁定始化由来

在Java程序中,有时需要推迟一些高开销的检查对象的初始化操作,并且只有在真正使用到这个对象的和延时候,才进行初始化,迟初此时,双重锁定始化就需要延迟初始化技术。检查

延迟初始化的和延正确实现是需要一些技巧的,否则容易出现问题,迟初下面一一介绍。双重锁定始化

方案1

public class UnsafeLazyInit{ private static Instance instance;   public static Instance getInstance(){     if (instance == null){          instance = new Instance();      }      return instance;  } }   

这种做法的检查错误是很明显的,如果两个线程分别调用getInstance,和延由于对共享变量的迟初访问没有做同步,很容易出现下面两种情况:

1.线程A和B都看到instance没有初始化,双重锁定始化于是检查分别进行了初始化。

2.instance=new Instance操作被重排序,和延实际执行过程可能是:先分配内存,然后赋值给instance,***再执行初始化。如果是这样的话,站群服务器其他线程可能就会读取到尚未初始化完成的instance对象。

方案2

public class UnsafeLazyInit{ private static Instance instance; public static synchronized Instance getInstance(){     if (instance == null){          instance = new Instance();      }      return instance;  } } 

这种做法的问题是很明显的,每一次读取instance都需要同步,可能会对性能产生较大的影响。

方案3

方案3是一个错误的双重检测加锁实现,看代码:

public class UnsafeLazyInit{ private static Instance instance; public static Instance getInstance(){     if (instance == null){          synchronized(UnsafeLazyInit.classs){              if (instance == null){                   instance = new Instance();                }           }      }      return instance;   } } 

这种方案看似解决了上面两种方案都存在的问题,但是也是有问题的。

问题根源

instance = new Instance();

这一条语句在实际执行中,可能会被拆分程三条语句,如下:

memory = allocate(); ctorInstance(memory); //2 instance = memory; //3 

根据重排序规则,后两条语句不存在数据依赖,因此是可以进行重排序的。

重排序之后,就意味着,instance域在被赋值了之后,指向的对象可能尚未初始化完成,而instance域是一个静态域,可以被其他线程读取到,亿华云那么其他线程就可以读取到尚未初始化完成的instance域。

基于volatile的解决方案

要解决这个办法,只需要禁止语句2和语句3进行重排序即可,因此可以使用volatile来修改instance就能做到了。

private volatile static Instance instance;

因为Volatile语义会禁止编译器将volatile写之前的操作重排序到volatile之后。

基于类初始化的解决方案

Java语言规范规定,对于每一个类或者接口C ,都有一个唯一的初始化锁LC与之对应,从C到LC的映射,由JVM实现。每个线程在读取一个类的信息时,如果此类尚未初始化,则尝试获取LC去初始化,如果获取失败则等待其他线程释放LC。如果能获取到LC,则要判断类的初始化状态,如果是位初始化,则要进行初始化。如果是正在初始化,则要等待其他线程初始化完成,如果是已经初始化,则直接使用此类对象。云服务器提供商

public class InstanceFactory{     private static class InstanceHolder{         public static Instance = new Instance();      }     public static Instance getInstance(){         return InstanceHolder.instance; //这里将导致instance类被初始化     }     } 

结论

字段延迟初始化降低了初始化类或者创建实例的开销,但是增加零访问被延迟促使化的字段的开销。在大部分时候,正常的初始化要优于延迟初始化。如果确实需要对实例字段使用线程安全的延迟初始化,请使用上面介绍的基于volatile的延迟初始化方案;如果确实需要对静态字段使用线程安全的延迟初始化,请使用上面基于类初始化方案的延迟初始化。

本文地址:http://www.bhae.cn/html/6c22499769.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

全站热门

你可能会有很多理由想要把一个应用、一个用户或者一个环境与你的 linux 系统隔离开来。不同的操作系统有不同的实现方式,而在 linux 中,一个典型的方式就是 chroot 环境。在这份教程中,我会一步一步指导你怎么使用 chroot 命令去配置一个与真实系统分离出来的独立环境。这个功能主要可以用于测试项目,以下这些步骤都在 Ubuntu 14.04 虚拟专用服务器(VPS)上执行。学会快速搭建一个简单的 chroot 环境是一项非常实用的技能,绝大多数系统管理员都能从中受益。Chroot 环境一个 chroot 环境就是通过系统调用,将一个本地目录临时变成根目录。一般所说的系统根目录就是挂载点/,然而使用 chroot 命令后,你可以使用其它目录作为根目录。原则上,任何运行在 chroot 环境内的应用都不能访问系统中其他信息(LCTT译注:使用 chroot 把一个目录变成根目录,在里面运行的应用只能访问本目录内的文件,无法访问到目录外的文件。然而,运行在 chroot 环境的应用可以通过 sysfs 文件系统访问到环境外的信息,所以,这里有个“原则上”的修饰语)。Chroot 环境的用处         测试一个不稳定的应用服务不会影响到整个主机系统。         就算使用 root 权限做了些不当的操作,把 chroot 环境搞得一塌糊涂,也不会影响到主机系统。         可以在你的系统中运行另外一个操作系统。举个例子,你可以在 chroot 环境中编译、安装、测试软件,而不去动真实的系统。你也可以在64位环境下使用 chroot 创建一个32位环境,然后运行一个32位的程序(LCTT泽注:假如你的真实环境是32位的,那就不能 chroot 一个64位的环境了)。但是为了安全考虑,chroot 环境为非特权用户设立了非常严格的限制,而不是提供完整的安全策略。假如你需要的是有完善的安全策略的隔离方案,可以考虑下 LXC、Docker、vservers等等。Debootstrap 和 Schroot使用 chroot 环境需要安装 debootstrap 和 schroot,这两个软件都在 Ubuntu 的镜像源中。其中 schroot 用于创建 chroot 环境。Debootstrap 可以让你通过镜像源安装任何 Debian(或基于 Debian 的)系统,装好的系统会包含最基本的命令。Schroot 命令允许用户使用相同的机制去创建 chroot 环境,但在访问 chroot 环境时会做些权限检查,并且会允许用户做些额外的自动设置,比如挂载一些文件系统。在 Ubuntu 14.04 LTS 上,我们可以通过两步来实现这个功能:1. 安装软件包第一步,在Ubuntu 14.04 LTS 主机系统上安装 debootstrap 和 schroot:复制代码代码如下:$ sudo apt-get install debootstrap $ sudo apt-get install schroot2. 配置 Schroot现在我们有工具在手,需要指定一个目录作为我们的 chroot 环境。这里创建一个目录先:复制代码代码如下:sudo mkdir /linoxide编辑 schroot 的配置文件:复制代码代码如下:sudo nano /etc/schroot/schroot.conf再提醒一下,我们现在是在 Ubuntu 14.04 LTS 系统上。假如我们想测试一个软件包能不能在 Ubuntu 13.10(代号是“Saucy Salamander”) 上运行,就可以在配置文件中添加下面的内容:复制代码代码如下: [saucy] description=Ubuntu Saucy location=/linoxide priority=3 users=arun root-groups=root 根据你的系统要求,调整上面的配置信息。3. 使用 debootstrap 安装32位 Ubuntu 系统Debootstrap 命令会在你的 chroot 环境里面下载安装一个最小系统。只要你能访问镜像源,你就可以安装任何基于 Debian 的系统版本。前面我们已经创建了 /linoxide 目录用于放置 chroot 环境,现在我们可以在这个目录里面运行 debootstrap 了:复制代码代码如下:cd /linoxide sudo debootstrap --variant=buildd --arch amd64 saucy /linoxide/ http://archive.ubuntu.com/ubuntu/ sudo chroot /linoxide /debootstrap/debootstrap --second-stage你可以将 --arch 的参数换成 i386 或其他架构,只要存在这种架构的镜像源。你也可以把镜像源 http://archive.ubuntu.com/ubuntu/ 换成离你最近的镜像源,具体可参考 Ubuntu 官方镜像主页。注意:假如你是在64位系统中创建32位系统,你需要在上面第3行命令中加入 --foreign 选项,就像下面的命令:复制代码代码如下:sudo debootstrap --variant=buildd --foreign --arch i386 saucy /linoxide/ http://archive.ubuntu.com/ubuntu/下载需要一段时间,看你网络带宽性能。最小系统大概有500M。4. 完成 chroot 环境安装完系统后,我们需要做一些收尾工作,确保系统运行正常。首先,保证主机的 fstab 程序能意识到 chroot 环境的存在:复制代码代码如下: sudo nano /etc/fstab在文件最后面添加下面的配置:复制代码代码如下:proc /linoxide/proc proc defaults 0 0 sysfs /linoxide/sys sysfs defaults 0 0保存并关闭文件。挂载一些文件系统到 chroot 环境:复制代码代码如下:$ sudo mount proc /linoxide/proc -t proc $ sudo mount sysfs /linoxide/sys -t sysfs复制 /etc/hosts 文件到 chroot 环境,这样 chroot 环境就可以使用网络了:复制代码代码如下:$ sudo cp /etc/hosts /linoxide/etc/hosts最后使用 schroot -l 命令列出系统上所有的 chroot 环境:复制代码代码如下:$ schroot -l使用下面的命令进入 chroot 环境:复制代码代码如下:$ sudo chroot /linoxide/ /bin/bash测试安装的版本:复制代码代码如下:# lsb_release -a # uname -a为了在 chroot 环境中使用图形界面,你需要设置 DISPLAY 环境变量:复制代码代码如下:$ DISPLAY=:0.0 ./apps目前为止,我已经成功地在 Ubuntu 14.04 LTS 上安装了 Ubuntu 13.10。退出 chroot 环境:复制代码代码如下:# exit清理一下,卸载文件系统:复制代码代码如下:$ sudo umount /test/proc $ sudo umount /test/sys

用Go轻松完成一个XA分布式事务,保姆级教程

结合异步迭代器实现 Node.js 流式数据复制

微服务架构下的熔断框架:Hystrix-Go

小新超极本(性的设计、强大的处理能力和出色的用户体验)

IDEA 写代码防沉迷了?

避不开的 setState 问题

OpenHarmony Neptune开发板I2C驱动OLED屏幕显示

友情链接

滇ICP备2023000592号-9