从LongAdder 看更高效的无锁实现
来源:互联网 发布:1553b数据总线 编辑:程序博客网 时间:2024/05/17 21:49
接触到AtomicLong的原因是在看guava的LoadingCache相关代码时,关于LoadingCache,其实思路也非常简单清晰:用模板模式解决了缓存不命中时获取数据的逻辑,这个思路我早前也正好在项目中使用到。
final
void
retryUpdate(
long
x, HashCode hc,
boolean
wasUncontended) {
int
h = hc.code;
boolean
collide =
false
;
// True if last slot nonempty
for
(;;) {
Cell[] as; Cell a;
int
n;
long
v;
if
((as = cells) !=
null
&& (n = as.length) >
0
) {
// 分支1
if
((a = as[(n -
1
) & h]) ==
null
) {
if
(busy ==
0
) {
// Try to attach new Cell
Cell r =
new
Cell(x);
// Optimistically create
if
(busy ==
0
&& casBusy()) {
boolean
created =
false
;
try
{
// Recheck under lock
Cell[] rs;
int
m, j;
if
((rs = cells) !=
null
&&
(m = rs.length) >
0
&&
rs[j = (m -
1
) & h] ==
null
) {
rs[j] = r;
created =
true
;
}
}
finally
{
busy =
0
;
}
if
(created)
break
;
continue
;
// Slot is now non-empty
}
}
collide =
false
;
}
else
if
(!wasUncontended)
// CAS already known to fail
wasUncontended =
true
;
// Continue after rehash
else
if
(a.cas(v = a.value, fn(v, x)))
break
;
else
if
(n >= NCPU || cells != as)
collide =
false
;
// At max size or stale
else
if
(!collide)
collide =
true
;
else
if
(busy ==
0
&& casBusy()) {
try
{
if
(cells == as) {
// Expand table unless stale
Cell[] rs =
new
Cell[n <<
1
];
for
(
int
i =
0
; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
}
finally
{
busy =
0
;
}
collide =
false
;
continue
;
// Retry with expanded table
}
h ^= h <<
13
;
// Rehash h ^= h >>> 17;
h ^= h <<
5
;
}
else
if
(busy ==
0
&& cells == as && casBusy()) {
//分支2
boolean
init =
false
;
try
{
// Initialize table
if
(cells == as) {
Cell[] rs =
new
Cell[
2
];
rs[h &
1
] =
new
Cell(x);
cells = rs;
init =
true
;
}
}
finally
{
busy =
0
;
}
if
(init)
break
;
}
else
if
(casBase(v = base, fn(v, x)))
break
;
// Fall back on using base
}
hc.code = h;
// Record index for next time
}
可以看到,LongAdder确实用了很多心思减少并发量,并且,每一步都是在”没有更好的办法“的时候才会选择更大开销的操作,从而尽可能的用最最简单的办法去完成操作。追求简单,但是绝对不粗暴。
————————————————分割线——————————————————————-
昨天在coolshell 投稿后 (文章在这里) 和左耳朵耗子简单讨论了下,发现左耳朵耗子对读者思维的引导还是非常不错的,在第一次发现这个类后,对里面的实现又提出了更多的问题,引导大家思考,值得学习,赞一个~
我们 发现的问题有这么几个:
1. jdk 1.7中是不是有这个类?
我确认后,结果如下: jdk-7u51版本上 上还没有 但是jdk-8u20 版本上已经有了。代码基本一样 ,增加了对double类型的支持和删除了一些冗余的代码。
2. base有没有参与汇总?
base在调用intValue等方法的时候是会汇总的:
3. base的顺序可不可以调换?
左耳朵耗子,提出了这么一个问题: 在add方法中,如果cells不会为空后,casBase方法一直都没有用了?
因此,我想可不可以调换add方法中的判断顺序,比如,先做casBase的判断,结果是 不调换可能更好,调换后每次都要CAS一下,在高并发时,失败几率非常高,并且是恶性循环,比起一次判断,后者的开销明显小很多,还没有副作用。因此,不调换可能会更好。
4. AtomicLong可不可以废掉?
我的想法是可以废掉了,因为,虽然LongAdder在空间上占用略大,但是,它的性能已经足以说明一切了,无论是从节约空的角度还是执行效率上,AtomicLong基本没有优势了,具体看这个测试(感谢coolshell读者Lemon的回复):http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/
- 从LongAdder看更高效的无锁实现
- 从LongAdder 看更高效的无锁实现
- 从LongAdder看更高效的无锁实现
- 从LongAdder看更高效的无锁实现
- 比AtomicLong还高效的LongAdder 源码解析
- 比AtomicLong还高效的LongAdder 源码解析
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现(disruptor)
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- 一种高效无锁内存队列的实现
- Linux下父子进程拾遗
- 推荐算法—协同过滤
- linux shell 中"2>&1"含义
- 变量作用域----Python高级语言特性(1)
- MFC中CFileDialog打开和保存文件对话框
- 从LongAdder 看更高效的无锁实现
- Linux 启动报错,/dev/mapper/vg_freeswitch-lv_root UNEXPECTED INCONSISTENCY;RUN fsck MANUALLY
- hadoop集群默认配置和常用配置
- 反省自己
- i2c时序图形
- JAVA中什么叫访问控制?
- 数据库基本--SQL语句大全
- Linux命令之nohup
- MFC ComboBox的使用