volatile - 如何实现线程安全

news/2024/7/5 4:38:51

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

volatile关键字,在我之前的博客 Volatile - 用途 中已经简单讲解过,当时提出了volatile在多线程中是不安全的。本文将重点介绍一种使用方式,可以实现线程安全。

首先,来看一段不安全的示例:

@NotThreadSafepublic class UnsafeCachingFactorizer implements Servlet {private final AtomicReference<BigInteger> lastNumber = new AtomicReference();private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference();public void service(ServletRequest req, ServletResponse resp){BigInteger i = extractFromRequest(req);if (i.equals(lastNumber.get())){encodeIntoResponse(resp, lastFactors.get());} else {BigInteger[] factors = factor(i);lastNumber.set(i);lastFactors.set(factors);encodeIntoResponse(resp, factors);}}
}

上面的示例中,尽管lastNumber和lastFactors这些原子引用本身是安全的,但是无法保证lastFactors中缓存的因数之积等于在lastNumber中缓存的值。也就是说,lastNumber和lastFactors无法保证同一时间只有一个线程修改、无法同时更新,存在竞态条件。


针对上面的竞态条件,提出了一种修复方式:volatile - 可以考虑创建一个不可变的类来包含这些需要原子方式执行的操作数据。看代码:

@Immutable
class OneValueCache {private final BigInteger lastNumber;private final BigInteger[] lastFactors;public OneValueCache(BigInteger i, BigInteger[] factors) {lastNumber = i;lastFactors = Arrays.copyof(factors, factors.length);}public BigInteger[] getFactors(BigInteger i) {if (lastNumber == null || !lastNumber.equals(i)) {return null;} else {return Arrays.copyof(lastFactors, lastFactors.length);}}
}
@ThreadSafepublic class VolatileCachedFactorizer implements Servlet {private Volatile OneValueCache cache = new OneValueCache(null, null);public void service(ServletRequest req, ServletResponse resp){BigInteger i = extractFromRequest(req);BigInteger[] factors = cache.getFactors(i);if (factors == null){factors = factor(i);cache = new OneValueCache(i, factors);}encodeIntoResponse(resp, factors);}
}

对于在访问和更新多个相关变量时出现的竞态条件问题,可以通过将这些变量全部保存在一个不可变对象中来消除。如果是一个可变的对象,那必须使用锁来确保原子性。如果要更新这些变量,那么可以创建一个新的容器对象。

此处,OneValueCache是不可变的,并且在每条相应的代码路径中只会访问一次。通过使用包含多个状态变量的容器对象来维持不变性条件,并且使用一个volatile类型的引用来确保可见性,从而保证了线程安全。

转载于:https://my.oschina.net/xiaowangqiongyou/blog/1922840


http://lihuaxi.xjx100.cn/news/236496.html

相关文章

利用脚本生成GUID

实际上&#xff0c;可以使用一种非常简单的方法来生成 GUID&#xff0c;但这种方法近乎像是作弊。&#xff08;您可听清楚了&#xff0c;我们说的可是“近乎”。&#xff09;“Scriptlet.TypeLib”对象的设计用途是帮助您创建“Windows 脚本组件”&#xff08;实质上&#xff0…

垃圾回收机制:

2019独角兽企业重金招聘Python工程师标准>>> 垃圾回收机制&#xff1a; 1、GC通过确定对象是否被活动对象引用来确定是否收集该对象。 2、触发GC的条件 1)GC在优先级最低的线程中运行&#xff0c;一般在应用程序空闲即没有应用线程在运行时被调用。但下面的条件例外…

c 应用程序mysql_MySQL C 语言应用程序接口开发教程

从数据库中取回数据在这个实例中我们从表中取回数据。步骤&#xff1a;(1)创建连接(2)执行查询(3)获取结果集(4)提取所有可用的记录(5)释放结果集实例程序打印 writers 表中所有的记录(姓名)。#include #include int main(int argc, char * argv[]){MYSQL * conn;MYSQL_RES * r…

快速开发生命周期支持工具

去年有幸研究公司管理产品的软件平台,当时考虑的产品是基于业务模型之上的一个系统平台,从建模到运行、部署、维护全生命周期管理.当时就提出两个希望先实践解决的就是可复用原型的设计和使用工作流和规则引擎的协作处理询标产品.前一阵子在公司研究成本产品的成本分析和算法,一…

SQL SERVER中什么情况会导致索引查找变成索引扫描

原文:SQL SERVER中什么情况会导致索引查找变成索引扫描SQL Server 中什么情况会导致其执行计划从索引查找&#xff08;Index Seek&#xff09;变成索引扫描&#xff08;Index Scan&#xff09;呢&#xff1f; 下面从几个方面结合上下文具体场景做了下测试、总结、归纳。 1&…

oracle rac对心跳要求_关于心跳网络引起的Oracle RAC的节点驱逐(不是实例驱逐)...

关于心跳网络引起的Oracle RAC的节点驱逐(不是实例驱逐)问&#xff1a;假设如下场景&#xff1a;4个节点rac&#xff0c;心跳线走的是千m网络交换机&#xff0c;若是该千M网络交换机断电&#xff0c;我想知道crs的驱逐节点的算法是怎么样的&#xff1f;Oracle 大连 GCS 答复&am…

qt练习11 鼠标,按键,滚轮事件学习

源代码: http://files.cnblogs.com/hnrainll/event.zip

python3 tkinter电子书_python3 tkinter实现添加图片和文本

本文在前面文章基础上介绍tkinter添加图片和文本&#xff0c;在这之前&#xff0c;我们需要安装一个图片库&#xff0c;叫Pillow&#xff0c;这个需要下载exe文件&#xff0c;根据下面图片下载和安装。下载完后直接双击安装exe&#xff0c;默认点击下一步&#xff0c;直到安装完…