java object多大 java对象内存模型 数组有多长(八)多线程cas

0 背景

java object多大 java对象内存模型 数组有多长(四)已经访问的对象记录优化

中,用byte数组处理,现在它将暴露在多线程中

 

借鉴concurrenthashmap的做法

Unsafe控制ConcurrentHashMap内并发数组元素的可见性

 

1双检分段锁

get

if yes, then return

if no, then lock the index of byte []

这里有点与concurrenthashmap不同,

第一 我们是单向双检,一旦bit是1就不用锁了,直接判断已经计算过该对象内存

第二,我们的元素是byte,而且鉴于该容器内存消耗考虑,只会用基本数据类型

基本数据类型不可锁,如果另外用对象锁 又会有大量的额外内存开销

 

2 cas

2.1 compareandsetbyte

发现native只有int级别的compareandset,因此我们要扩大cas的范围从byte到byte所在int,所以就要求byte[]的长度是4的倍数,同时当然冲突域也相应的从本来就已经从bit扩到byte的再次扩大到了32位,造成不必要的冲突

if(enhanceVisited) {
if(obj != null) {
if(cas) {
int hash = System.identityHashCode(obj);
int index = hash / 8;
int pos = hash & 7;
int indexInt = hash / 32;
int posInt = hash & 31;

while (true) {
byte tmp = unsafe.getByteVolatile(pool, arrayBaseOffset + index);
if ((tmp & (1 << pos)) > 0) {
isRecursive = visited = true;
if (findCAS)
println(Thread.currentThread() + " already has " + hash + " " + tmp);
break;
}

int origin = unsafe.getIntVolatile(pool, arrayBaseOffset + indexInt * 4);
if (findCAS) {
try {
println(Thread.currentThread() + " origin value " + hash + " " + origin);
Thread.sleep(new Random().nextInt(20) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int mod = origin | (1 << posInt);
if (unsafe.compareAndSwapInt(pool, arrayBaseOffset + indexInt * 4, origin, mod)) {
byte tmp2 = unsafe.getByteVolatile(pool, arrayBaseOffset + index);
// other threads might change the byte, but must not change the bit from 1 back to 0
if (!((tmp2 & (1 << pos)) > 0))
throw new RuntimeException();
if (findCAS)
println(Thread.currentThread() + "set success " + hash + " " + origin + " " + mod);
break;
} else {
if (findCAS)
println(Thread.currentThread() + "set fail " + hash + " " + origin + " " + mod);
}
}
} else {
int hash = System.identityHashCode(obj);
int index = hash / 8;

byte tmp = unsafe.getByteVolatile(pool, index);

if (findCAS) {
try {
Thread.sleep(new Random().nextInt(20) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int pos = hash & 7;
if((tmp & (1<<pos)) > 0) {
isRecursive = visited = true;
} else {
tmp |= (1 << pos);
unsafe.putByteVolatile(pool, index, tmp);
}
}
}
} else {
if (m_visited.containsKey(obj))
isRecursive = visited = true;
m_visited.put(obj, true);
}

2.2

- fail后是否sleep?

判断不应该有高频集中并发,所以不sleep

 

- byte [] 本身是否需要volatile?

不需要 concurrenthashmap涉及到扩容重新配数组,所以需要,我们是定长的final 数组

 

2.3 测试

开10个线程,对同一个Double计算

在getorigin与compareandset中间加上random sleep,使线程错开

2.3.1

Thread[Thread-4,5,main] origin value 445635754 0
Thread[Thread-1,5,main] origin value 445635754 0
Thread[Thread-2,5,main] origin value 445635754 0
Thread[Thread-3,5,main] origin value 445635754 0
Thread[Thread-5,5,main] origin value 445635754 0
Thread[Thread-0,5,main] origin value 445635754 0
Thread[Thread-6,5,main] origin value 445635754 0
Thread[Thread-7,5,main] origin value 445635754 0
Thread[Thread-8,5,main] origin value 445635754 0
Thread[Thread-6,5,main]set success 445635754 0 1024
Thread[Thread-9,5,main] already has 445635754 4

除线程9外,其余9个线程进入临界区

线程6获得锁

Thread[Thread-8,5,main]set fail 445635754 0 1024
Thread[Thread-8,5,main] already has 445635754 4
Thread[Thread-4,5,main]set fail 445635754 0 1024
Thread[Thread-4,5,main] already has 445635754 4
Thread[Thread-3,5,main]set fail 445635754 0 1024
Thread[Thread-3,5,main] already has 445635754 4
Thread[Thread-5,5,main]set fail 445635754 0 1024
Thread[Thread-5,5,main] already has 445635754 4
Thread[Thread-7,5,main]set fail 445635754 0 1024
Thread[Thread-1,5,main]set fail 445635754 0 1024
Thread[Thread-7,5,main] already has 445635754 4
Thread[Thread-1,5,main] already has 445635754 4
Thread[Thread-0,5,main]set fail 445635754 0 1024
Thread[Thread-0,5,main] already has 445635754 4
Thread[Thread-2,5,main]set fail 445635754 0 1024
Thread[Thread-2,5,main] already has 445635754 4

其余8个线程返回while顶点重拿origin值

 

 2.3.2

BC 不考虑并发 24*10 = 240B

AC 开率并发 24B

 

3 让它去

则部分对象存在重复计算大小并累加的情况,产生误差

posted on 2024-06-19 15:13  silyvin  阅读(1)  评论(0编辑  收藏  举报