堆外并发计数器
来源:互联网 发布:js canvas 动画库 编辑:程序博客网 时间:2024/05/22 17:43
堆外并发计数器
本文由 ImportNew - 人晓 翻译自 javacodegeeks。欢迎加入翻译小组。转载请参见文章末尾的要求。
并发计数器几乎每个系统都有,它通常用来收集数据、实现线程同步。Java对于基于堆的计数器有很好的支持。
在某些情况下,需要建立多个进程共享的计数器。
如何建立进程间的计数器?
数据库
数据库是我们最容易想到的方法,数据库索引是一个可以被多进程共享的计数器。所有的并发都由数据库处理。对于初学者来说这是一个不错的选择,但是我们知道数据库会降低网络、锁等一些方面的性能。只有Larry Elision(译者注:Oracle的CEO)会对此感到高兴,你不会的。
服务器
你可以开发服务器或中间件提供这类服务,不过这种方法仍然会存在网络、序列化/反序列化方面的开销。
内存映射文件
你可以通过使用内存映射文件解决这一问题。我在听了PeterLawrey关于“Java中共享内存的进程间的线程安全”演讲之后想到了这个方法。
多进程计数器中遇到的困难
数据可视化
进程对数据的改变对于其他进程是可见的。这个问题可以通过使用内存映射文件解决,操作系统可以保证这一功能的实现,Java的内存模式也能支持这种方式。
线程安全
由于计数器有多个写入者,这使得线程安全成了一个很大的问题。比较与交换(Compare-and-swap,CAS)可以用来解决多个写入者的问题。但是,是否可以使用CAS实现堆外操作呢?答案是肯定的,欢迎使用Unsafe类。通过使用内存映射与Unsafe类结合可以使用CAS实现堆外操作。
在这篇博客中我将向大家分享我使用CAS实现内存映射的经验。
到底如何实现的?
怎样或得内存地址?
MappedByteBuffer使用DirectByteBuffer,其实是堆外内存。所以,我们可以获得内存的虚拟地址,并使用Unsafe类实现CAS操作。请看下面的代码:
FileChannel fc =
new
RandomAccessFile(fileName,
"rw"
).getChannel();
// Map 8 bytes for long value.
mem = fc.map(FileChannel.MapMode.READ_WRITE,
0
,
8
);
startAddress = ((DirectBuffer) mem).address();
上面的代码创建了八字节的内存映射文件,同时获得了它的虚拟地址。这个地址可以用来读写内存映射文件中的内容。
如何实现线程安全的读写?
public
boolean
increment() {
long
orignalValue = readVolatile(startAddress);
long
value = convert(orignalValue);
return
UnsafeUtils.unsafe.compareAndSwapLong(
null
,
startAddress,orignalValue, convert(value +
1
));
}
public
long
get() {
long
orignalValue = readVolatile(startAddress);
return
convert(orignalValue);
}
// Only unaligned is implemented
private
static
long
readVolatile(
long
position) {
if
(UnsafeUtils.unaligned()) {
return
UnsafeUtils.unsafe.getLongVolatile(
null
, position);
}
throw
new
UnsupportedOperationException();
}
private
static
long
convert(
long
a) {
if
(UnsafeUtils.unaligned()) {
return
(UnsafeUtils.nativeByteOrder ? a : Long.reverseBytes(a));
}
throw
new
UnsupportedOperationException();
}
重点看一下readVolatile和increment函数,readVolatile直接从内存读取内容,increment 使用Unsafe类在MemoryByteBuffer得到的地址上实现CAS操作。
性能
下面是系统的性能参数。每个线程计数器统计一百万次。
计数器的性能在下降,随着线程数量的增加CAS操作的失败次数也开始增加,性能开始下降。这些计数器的性能可以通过资源分割减少资源来提高。我会在下一篇博文中详细介绍。
结论
- 内存映射文件十分有效,它可以用来开发一系列东西,如堆外集合、IPC、堆外线程同步等;
- 内存映射文件减少了垃圾回收GC的编程。
博客中的所有代码可以在github中下载。
- 堆外并发计数器
- 并发性能计数器模块
- java并发计数器问题
- java 并发 (1):原子计数器
- redis计数器,redis并发锁
- jvm 堆、栈、方法区、程序计数器
- 解决Java多线程并发的计数器问题
- java并发编程(一):计数器
- 使用Redis计数器防止并发请求
- 使用Redis计数器防止并发请求
- 深谈多并发计数器CountDownLatch(一)
- 深谈多并发计数器CountDownLatch(二)
- Redis原子计数器incr,防止并发请求
- 使用redis计数器防止并发请求
- 并发杂堆
- 计数器
- 计数器
- 计数器
- 如何清除 xcode6的 描述文件
- 使用huhamhire-hosts解决无法访问google账户以及Gmail的问题
- stream_iterator和ostream_iterator学习体会
- asyncktask详解
- 插入排序的两种实现
- 堆外并发计数器
- I CAN DO IT
- 黑马程序员——关键字、标示符、注释
- 使用ratingbar评分的星星
- 并口定义及并口编程基础
- Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚
- sqlserver2008 服务 连接失败 -服务器名称如何写!!
- 模态视图中添加代码的具体方法
- JAVA生成Word文档(经过测试)