聊聊高并发(十八)理解AtomicXXX.lazySet方法
看过java.util.concurrent.atomic包里面各个AtomicXXX类实现的同学应该见过lazySet方法。比方AtomicBoolean类的lazySet方法
public final void lazySet(boolean newValue) {
int v = newValue ? 1 : 0;
unsafe.putOrderedInt(this, valueOffset, v);
}
它的底层实现调用了Unsafe的putOrderedInt方法。来看看putOrderedXXX方法的JavaDoc
它的意思是putOrderedXXX方法是putXXXVolatile方法的延迟实现,不保证值的改变被其它线程马上看到
<span><span><span style="font-family:Calibri;font-size:12px;"> /***
* Sets the value of the integer field at the specified offset in the
* supplied object to the given value. This is an ordered or lazy
* version of <code>putIntVolatile(Object,long,int)</code>, which
* doesn't guarantee the immediate visibility of the change to other
* threads. It is only really useful where the integer field is
* <code>volatile</code>, and is thus expected to change unexpectedly.
* 设置obj对象中offset偏移地址相应的整型field的值为指定值。这是一个有序或者
* 有延迟的<code>putIntVolatile</cdoe>方法。而且不保证值的改变被其它线程立
* 即看到。仅仅有在field被<code>volatile</code>修饰而且期望被意外改动的时候
* 使用才实用。 *
* @param obj the object containing the field to modify.
* 包括须要改动field的对象
* @param offset the offset of the integer field within <code>obj</code>.
* <code>obj</code>中整型field的偏移量
* @param value the new value of the field.
* field将被设置的新值
* @see #putIntVolatile(Object,long,int)
*/</span></span></span>
<span><span><span style="font-family:Calibri;font-size:12px;"> public native void putOrderedInt(Object obj, long offset, int value);</span></span></span>
理解volatile底层实现的同学知道,volatile的实现终于是加了内存屏障,
1. 保证写volatile变量会强制把CPU写缓存区的数据刷新到内存
2. 读volatile变量时,使缓存失效。强制从内存中读取最新的值
3. 由于内存屏障的存在,volatile变量还能阻止重排序
所以volatile变量的改动能够立马让全部的线程可见,保证了可见性。
而不加volatile变量的字段。JMM不保证普通变量的改动立马被全部的线程可见。所以lazySet说白了就是以普通变量的方式来写变量。
// 对flagA的改动对全部线程立马可见
volatile boolean flagA;
// 对flagB的改动不能立马被其它线程可见
boolean flagB;
那么为什么须要lazySet方法呢?事实上它是一种低级别的优化手段,对上层调用者来说。事实上非常少用到。以下说说用lazySet的一个场景。
在这篇 聊聊高并发(十六)实现一个简单的可重入锁 中我们实现了一个可重入锁。里面共享变量用了volatile变量,来保证对共享变量的改动对其它线程可见。
可是事实上,这里全然能够不用volatile变量来修饰这些共享状态。
1. 由于訪问共享状态之前先要获得锁, Lock.lock()方法能够获得锁,而获得锁的操作和volatile变量的读操作一样,会强制使CPU缓存失效,强制从内存读取变量。
2. Lock.unlock()方法释放锁时。和写volatile变量一样,会强制刷新CPU写缓冲区。把缓存数据写到主内存
底层也是通过加内存屏障实现的。
package com.zc.lock; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; /**
* 简单的可重入锁实现,使用一个计数器记录当前线程重入锁的次数,获得锁时计数器加1,释放锁时计数器减1。当计数器等于0时表示释放了锁
* **/
public class SimpleReentrantLock implements Lock{ // 指向已经获得锁的线程
private volatile Thread exclusiveOwnerThread; // 记录获取了同一个锁的次数
private volatile int holdCount; private final java.util.concurrent.locks.Lock lock; // 是否是自己获得锁的条件
private final Condition isCountZero; public SimpleReentrantLock(){
lock = new ReentrantLock();
isCountZero = lock.newCondition();
holdCount = 0;
} @Override
public void lock() {
lock.lock();
try{
// 当前线程的引用
Thread currentThread = Thread.currentThread();
// 假设获得锁的线程是自己。那么计数器加1,直接返回
if(exclusiveOwnerThread == currentThread){
holdCount ++;
return;
} while(holdCount != 0){
try {
isCountZero.await();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted");
}
}
// 将exclusiveOwnerThread设置为自己
exclusiveOwnerThread = currentThread;
holdCount ++;
}finally{
lock.unlock();
}
}
而lazySet()的使用方法和上面的优化是一个道理,就是在不须要让共享变量的改动立马让其它线程可见的时候,以设置普通变量的方式来改动共享状态,能够降低不必要的内存屏障,从而提高程序运行的效率。
以下的样例来自StackOverflow上的一个提问,说的也是相似的意思,就是优化不必要的volatile操作。
被墙的同学看不到。能够看截图。
聊聊高并发(十八)理解AtomicXXX.lazySet方法的更多相关文章
- 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类
这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cu ...
- 聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁
这篇讲讲ReentrantReadWriteLock可重入读写锁,它不仅是读写锁的实现,而且支持可重入性. 聊聊高并发(十五)实现一个简单的读-写锁(共享-排他锁) 这篇讲了怎样模拟一个读写锁. 可重 ...
- 聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore
前几篇分析了一下AQS的原理和实现.这篇拿Semaphore信号量做样例看看AQS实际是怎样使用的. Semaphore表示了一种能够同一时候有多个线程进入临界区的同步器,它维护了一个状态表示可用的票 ...
- 聊聊高并发(三十四)Java内存模型那些事(二)理解CPU快速缓存的工作原理
在上一篇聊聊高并发(三十三)从一致性(Consistency)的角度理解Java内存模型 我们说了Java内存模型是一个语言级别的内存模型抽象.它屏蔽了底层硬件实现内存一致性需求的差异,提供了对上层的 ...
- 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁
上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和基本的方法,显示了怎样 ...
- 聊聊高并发(三十二)实现一个基于链表的无锁Set集合
Set表示一种没有反复元素的集合类,在JDK里面有HashSet的实现,底层是基于HashMap来实现的.这里实现一个简化版本号的Set,有下面约束: 1. 基于链表实现.链表节点依照对象的hashC ...
- 高并发第八弹:J.U.C起航(java.util.concurrent)
java.util.concurrent是JDK自带的一个并发的包主要分为以下5部分: 并发工具类(tools) 显示锁(locks) 原子变量类(aotmic) 并发集合(collections) ...
- 从SpringBoot构建十万博文聊聊高并发文章浏览量设计
前言 在经历了,缓存.限流.布隆穿透等等一系列加强功能,十万博客基本算是成型,网站上线以后也加入了百度统计来见证十万+ 的整个过程. 但是百度统计并不能对每篇博文进行详细的浏览量统计,如果做一些热点博 ...
- java高并发----个人学习理解汇总记录
1.首先,需要理解几个概念 1.同步(Synchronous):同步方法调用一旦开始,调用者必须等到前面的方法调用返回后,才能继续后续的行为,依次直到完成所有. 2.异步(Asynchronous): ...
随机推荐
- 最简单的TCP程序
网络编程里面最简单的一个tcp程序,把一个文件从客户端上传到服务器端,上传成功后服务器端提示上传完成.代码不多,一个服务端和一个客户端. 服务端代码: import java.io.FileOutpu ...
- 机器学习(一) K-means聚类
聚类算法K-means是硬聚类算法,是目标函数聚类算法的代表.K-means算法以欧式距离作为相似度测度,它是求对应某一初始聚类中心向量V最优分类,使得评价指标J最小.算法采用误差平方和准则函数作为聚 ...
- Linux FTP客户端
1.File Zilla File Zilla是一个开源的,跨平台的Linux FTP客户端.File Zilla有一个标签式的用户界面,允许用户查看正在传输的文件的所有细节.File Zilla是通 ...
- php八大设计模式之职责链模式
当发生一种事情时,我们需要要对应职责的事物去处理对应的事情. 或者去找最近的类(就是级别最低的)去解决,如果解决不了就顺着往上找职责更高的,直到解决为止. 注意:一定是要有一个职责最高的类,否则会有问 ...
- 洛谷3961 [TJOI2013]黄金矿工
题目描述 小A最近迷上了在上课时玩<黄金矿工>这款游戏.为了避免被老师发现,他必须小心翼翼,因此他总是输.在输掉自己所有的金币后,他向你求助.每个黄金可以看做一个点(没有体积).现在给出你 ...
- 参考《深度学习之PyTorch实战计算机视觉》PDF
计算机视觉.自然语言处理和语音识别是目前深度学习领域很热门的三大应用方向. 计算机视觉学习,推荐阅读<深度学习之PyTorch实战计算机视觉>.学到人工智能的基础概念及Python 编程技 ...
- Linux中常用命令(文件)
1.cat 显示出文件的全部内容 (1)格式:cat 文件名 -n 显示行号 (2)特点:一次性显示所有文件内容 2.tac 从最后一行倒着显示文件全部内容 3.more 全屏方式分页显示文件内容 回 ...
- numpy基础篇-简单入门教程1
np.split(A, 4, axis=1),np.hsplit(A, 4) 分割 A = np.arange(12).reshape((3, 4)) # 水平方向的长度是4 print(np.spl ...
- Java基础学习总结(15)——java读取properties文件总结
一.java读取properties文件总结 在java项目中,操作properties文件是经常要做的,因为很多的配置信息都会写在properties文件中,这里主要是总结使用getResource ...
- OC便利方法
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { #if // 从面向对象的角度 ...