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 让它去
则部分对象存在重复计算大小并累加的情况,产生误差