聊聊高并发(十八)理解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): ...
随机推荐
- .Net配置虚拟域名
1.在IIS中配置和地址端口,和名称. 2.在hosts文件中加上地址匹配. 3.重启IIS管理网站. 就可以通过虚拟域名进行访问了.
- ★★★【卡法 常用js库】: js汇合 表单验证 cookie设置 日期格式 电话手机号码 email 整数 小数 金额 检查参数长度
[卡法 常用js库]: js汇合 表单验证 cookie设置 日期格式 电话手机号码 email 整数 小数 金额 检查参数长度 // +---------------------- ...
- matlab2017a doc 关联注册码
在 matlab 2017a 的命令行界面,输入doc **查看相关函数的帮助文档时,必须要关联注册码才可使用. 这种显然是在网络连接状况下给出的提示,也即主机处在网络连接状态,试图默认查找的网络中的 ...
- Random words
To choose a random word from the histogram, the simplest algorithm is to build a list with multiple ...
- Traversing a list
The most common way to traverse the elements of a list is with a for loop. The syntax is the same as ...
- Liunx-php7安装swoole扩展
Liunx-php7安装swoole扩展 标签(空格分隔): php 下载包 https://github.com/swoole/swoole-src/releases 安装过程 直接wget也行直接 ...
- OpenGL编程逐步深入(十一)组合变换
准备知识 在前面的几节教程中,我们已经提到过几种变换,为物体在3D世界中的移动提供的极大的灵活性.但是我们还有很多东西需要学习(如摄像机控制和透视投影),你可以已经猜到,我们需要將这些变换组合起来.在 ...
- JavaScript总结(4)
如何绑定事件 程序员可以编写代码,要求页面在发生了某些事件时调用相应的JavaScript语句或函数,这被称为事件的绑定.事件的绑定有3种方式.1)在HTML标记中直接声明,这是最常见的一种做法.语法 ...
- Vue.js小demo
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 几个提高效率的PHOTOSHOP秘密快捷键
1.拖动选择 使用矩形选框工具,在画布上拖动(不要松开鼠标),这时按住空格键,然后移动鼠标,你会发现选区也跟着移动了. 2.左右流量文档 按住Cmd(Ctrl)键,上下滚动鼠标,你会发现文档的滚动条在 ...