java安全编码指南之:锁的双重检测
简介
双重检测锁定模式是一种设计模式,我们通过首次检测锁定条件而不是实际获得锁从而减少获取锁的开销。
双重检查锁定模式用法通常用于实现执行延迟初始化的单例工厂模式。延迟初始化推迟了成员字段或成员字段引用的对象的构造,直到实际需要才真正的创建。
但是我们需要非常小心的使用双重检测模式,以避免发送错误。
单例模式的延迟加载
先看一个在单线程正常工作的单例模式:
public class Book {
private static Book book;
public static Book getBook(){
if(book==null){
book = new Book();
}
return book;
}
}
上面的类中定义了一个getBook方法来返回一个新的book对象,返回对象之前,我们先判断了book是否为空,如果不为空的话就new一个book对象。
初看起来,好像没什么问题,我们仔细考虑一下:
book=new Book()其实一个复杂的命令,并不是原子性操作。它大概可以分解为1.分配内存,2.实例化对象,3.将对象和内存地址建立关联。
在多线程环境中,因为重排序的影响,我们可能的到意向不到的结果。
最简单的办法就是加上synchronized关键字:
public class Book {
private static Book book;
public synchronized static Book getBook(){
if(book==null){
book = new Book();
}
return book;
}
}
double check模式
如果要使用double check模式该怎么做呢?
public class BookDLC {
private static BookDLC bookDLC;
public static BookDLC getBookDLC(){
if(bookDLC == null ){
synchronized (BookDLC.class){
if(bookDLC ==null){
bookDLC=new BookDLC();
}
}
}
return bookDLC;
}
}
我们先判断bookDLC是否为空,如果为空,说明需要实例化一个新的对象,这时候我们锁住BookDLC.class,然后再进行一次为空判断,如果这次不为空,则进行初始化。
那么上的代码有没有问题呢?
有,bookDLC虽然是一个static变量,但是因为CPU缓存的原因,我们并不能够保证当前线程被赋值之后的bookDLC,立马对其他线程可见。
所以我们需要将bookDLC定义为volatile,如下所示:
public class BookDLC {
private volatile static BookDLC bookDLC;
public static BookDLC getBookDLC(){
if(bookDLC == null ){
synchronized (BookDLC.class){
if(bookDLC ==null){
bookDLC=new BookDLC();
}
}
}
return bookDLC;
}
}
静态域的实现
public class BookStatic {
private static BookStatic bookStatic= new BookStatic();
public static BookStatic getBookStatic(){
return bookStatic;
}
}
JVM在类被加载之后和被线程使用之前,会进行静态初始化,而在这个初始化阶段将会获得一个锁,从而保证在静态初始化阶段内存写入操作将对所有的线程可见。
上面的例子定义了static变量,在静态初始化阶段将会被实例化。这种方式叫做提前初始化。
下面我们再看一个延迟初始化占位类的模式:
public class BookStaticLazy {
private static class BookStaticHolder{
private static BookStaticLazy bookStatic= new BookStaticLazy();
}
public static BookStaticLazy getBookStatic(){
return BookStaticHolder.bookStatic;
}
}
上面的类中,只有在调用getBookStatic方法的时候才会去初始化类。
ThreadLocal版本
我们知道ThreadLocal就是Thread的本地变量,它实际上是对Thread中的成员变量ThreadLocal.ThreadLocalMap的封装。
所有的ThreadLocal中存放的数据实际上都存储在当前线程的成员变量ThreadLocal.ThreadLocalMap中。
如果使用ThreadLocal,我们可以先判断当前线程的ThreadLocal中有没有,没有的话再去创建。
如下所示:
public class BookThreadLocal {
private static final ThreadLocal<BookThreadLocal> perThreadInstance =
new ThreadLocal<>();
private static BookThreadLocal bookThreadLocal;
public static BookThreadLocal getBook(){
if (perThreadInstance.get() == null) {
createBook();
}
return bookThreadLocal;
}
private static synchronized void createBook(){
if (bookThreadLocal == null) {
bookThreadLocal = new BookThreadLocal();
}
perThreadInstance.set(bookThreadLocal);
}
}
本文的代码:
learn-java-base-9-to-20/tree/master/security
本文已收录于 http://www.flydean.com/java-security-code-line-double-check-lock/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
java安全编码指南之:锁的双重检测的更多相关文章
- java安全编码指南之:基础篇
目录 简介 java平台本身的安全性 安全第一,不要写聪明的代码 在代码设计之初就考虑安全性 避免重复的代码 限制权限 构建可信边界 封装 写文档 简介 作为一个程序员,只是写出好用的代码是不够的,我 ...
- java安全编码指南之:Mutability可变性
目录 简介 可变对象和不可变对象 创建mutable对象的拷贝 为mutable类创建copy方法 不要相信equals 不要直接暴露可修改的属性 public static fields应该被置位f ...
- java安全编码指南之:字符串和编码
目录 简介 使用变长编码的不完全字符来创建字符串 char不能表示所有的Unicode 注意Locale的使用 文件读写中的编码格式 不要将非字符数据编码为字符串 简介 字符串是我们日常编码过程中使用 ...
- java安全编码指南之:输入校验
目录 简介 在字符串标准化之后进行校验 注意不可信字符串的格式化 小心使用Runtime.exec() 正则表达式的匹配 简介 为了保证java程序的安全,任何外部用户的输入我们都认为是可能有恶意攻击 ...
- java安全编码指南之:可见性和原子性
目录 简介 不可变对象的可见性 保证共享变量的复合操作的原子性 保证多个Atomic原子类操作的原子性 保证方法调用链的原子性 读写64bits的值 简介 java类中会定义很多变量,有类变量也有实例 ...
- java安全编码指南之:死锁dead lock
目录 简介 不同的加锁顺序 使用private类变量 使用相同的Order 释放掉已占有的锁 简介 java中为了保证共享数据的安全性,我们引入了锁的机制.有了锁就有可能产生死锁. 死锁的原因就是多个 ...
- java安全编码指南之:lock和同步的正确使用
目录 简介 使用private final object来作为lock对象 不要synchronize可被重用的对象 不要sync Object.getClass() 不要sync高级并发对象 不要使 ...
- java安全编码指南之:Thread API调用规则
目录 简介 start一个Thread 不要使用ThreadGroup 不要使用stop()方法 wait 和 await 需要放在循环中调用 简介 java中多线程的开发中少不了使用Thread,我 ...
- java安全编码指南之:文件和共享目录的安全性
目录 简介 linux下的文件基本权限 linux文件的特殊权限 Set UID 和 Set GID Sticky Bit SUID/SGID/SBIT权限设置 文件隐藏属性 特殊文件 java中在共 ...
随机推荐
- 使用枚举类Enum作为callee和caller的约定,运用反射消除分支和重复代码在命令式程序中的应用
在开发过程中,程序提供的功能由简单变得复杂,承担功能的主要类也会因此变得庞大臃肿,如果不加以维护,就会散发出浓重的代码味道.下面这篇博文,主要讲述了利用Enum,反射等手段简化重构代码的过程. 代码涉 ...
- .NET Core 下使用 Apollo 配置中心
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理场景.服务 ...
- LG P2389 电脑班的裁员
Description ZZY有独特的裁员技巧:每个同学都有一个考试得分$a_i(-1000 \leq a_i \leq 1000)$,在$n$个同学$(n \leq 500)$中选出不大于$k$段$ ...
- pytest文档1-pytest+Allure+jenkins+邮箱发送
前言: 1.pytest+allure是目前很多公司使用较多的一种报告样式,因为它更详细,各种指标更直观(简单的说就是看着更高大上,更能装X). 环境准备: 1.Windows10 2.Allure ...
- Bootstrap学习第二天轮播插件
Bootstrap不仅提供了整套的样式,还提供了一系列插件,帮助开发者构建更受欢迎的网站. Bootstrap的轮播插件用起来很简单,代码如下. <!DOCTYPE html> <h ...
- zabbix邮件报警设置(加密)
邮件设置如下 端口号选择465
- asp.net core 从 3.1 到 5.0
asp.net core 从 3.1 到 5.0 Intro 就在前几天,微软宣布了 .NET5 发布了 RC1 版本,这也意味着 .NET5 的开发基本稳定了,正式发布之前,不会再新增新的 Feat ...
- golang 指针类型学习
对于指针类型不安全编程,通过指针值以及偏移量操作slice私有属性 // 通过指针值获取切片 func TestGetPrivate(t *testing.T) { s := []int{1, 2, ...
- 容器云平台No.1~基于Docker及Kubernetes构建的容器云平台
开篇 最近整理笔记,不知不觉发现关于kubernetes相关的笔记已经达99篇了,索性一起总结了.算是对这两年做容器云平台的一个总结,本文是开篇,先介绍下所有用到的组件.首先来看下架构图(实在画的太丑 ...
- python爬取酷狗音乐
url:https://www.kugou.com/yy/html/rank.html 我们随便访问一个歌曲可以看到url有个hash https://www.kugou.com/song/#hash ...