Java中比较和交换的语义是什么?也就是说,一个
AtomicInteger
只需保证不同线程之间对原子整数实例的特定内存位置的有序访问,或者它是否保证对内存中所有位置的有序访问,即它的行为就像是一个易失性(内存栅栏)。
从
docs
:
-
weakCompareAndSet
原子地读取和有条件地写入变量,但不会在排序之前创建任何happens,因此对于除
黄鼠狼
.
-
compareAndSet
以及所有其他读取和更新操作,如
getAndIncrement
具有读写易失性变量的记忆效果。
从API文档中可以明显看出
比较集
就像是一个不稳定的变量。然而,
黄鼠狼
应该只是改变它的特定内存位置。因此,如果该内存位置对单个处理器的缓存是独占的,
弱compareandset
应该比平时快得多
比较集
.
我这么问是因为我已经通过运行
threadnum
不同的线程,不同的
螺纹数
从1到8,并且
totalwork=1e9
(代码是用Scala(一种静态编译的JVM语言)编写的,但它的含义和字节码翻译都与Java的含义和字节码翻译是同构的,在本例中,这一小段应该很清楚):
val atomic_cnt = new AtomicInteger(0)
val atomic_tlocal_cnt = new java.lang.ThreadLocal[AtomicInteger] {
override def initialValue = new AtomicInteger(0)
}
def loop_atomic_tlocal_cas = {
var i = 0
val until = totalwork / threadnum
val acnt = atomic_tlocal_cnt.get
while (i < until) {
i += 1
acnt.compareAndSet(i - 1, i)
}
acnt.get + i
}
def loop_atomic_weakcas = {
var i = 0
val until = totalwork / threadnum
val acnt = atomic_cnt
while (i < until) {
i += 1
acnt.weakCompareAndSet(i - 1, i)
}
acnt.get + i
}
def loop_atomic_tlocal_weakcas = {
var i = 0
val until = totalwork / threadnum
val acnt = atomic_tlocal_cnt.get
while (i < until) {
i += 1
acnt.weakCompareAndSet(i - 1, i)
}
acnt.get + i
}
在AMD上有4个2.8GHz双核和2.67GHz 4核i7处理器。JVM是Sun服务器热点JVM 1.6。结果显示没有性能差异。
规格:AMD82204X双核@2.8GHz
测试名称:loop_atomic_tlocal_cas
运行时间:(显示最后3个)
7504.562 7502.817 7504.626(平均值=7415.637最小值=7147.628最大值=7504.886)
运行时间:(显示最后3个)
3751.553 3752.589 3751.519(平均值=3713.5513最小值=3574.708最大值=3752.949)
运行时间:(显示最后3个)
1890.055 1889.813 1890.047(平均值=2065.7207最小值=1804.652最大值=3755.852)
运行时间:(显示最后3个)
960.12 989.453 970.842(平均值=1058.8776最小值=940.492最大值=1893.127)
测试名称:loop_atomic_weakcas
运行时间:(显示最后3个)
7325.425 7057.03 7325.407(平均值=7231.8682最小值=7057.03最大值=7325.45)
运行时间:(显示最后3个)
3663.21 3665.838 3533.406(平均值=3607.2149最小值=3529.177最大值=3665.838)
运行时间:(显示最后3个)
3664.163 1831.979 1835.07(平均值=2014.2086最小值=1797.997最大值=3664.163)
运行时间:(显示最后3个)
940.504928.467921.376(平均值=943.665最小值=919.985最大值=997.681)
测试名称:loop_atomic_tlocal_weakcas
运行时间:(显示最后3个)
7502.876 7502.857 7502.933(平均值=7414.8132最小值=7145.869最大值=7502.933)
运行时间:(显示最后3个)
3752.623 3751.53 3752.434(平均值=3710.1782最小值=3574.398最大值=3752.623)
运行时间:(显示最后3个)
1876.723 1881.069 1876.538(平均值=4110.4221最小值=1804.62最大值=12467.351)
运行时间:(显示最后3个)
959.329 1010.53 969.767(平均值=1072.8444最小值=959.329最大值=1880.049)
规格:英特尔i7四核@2.67 GHz
测试名称:loop_atomic_tlocal_cas
运行时间:(显示最后3个)
8138.3175 8130.0044 8130.1535(平均值=8119.2888最小值=8049.6497最大值=8150.1950)
运行时间:(显示最后3个)
4067.7399 4067.5403 4068.3747(平均值=4059.6344最小值=4026.2739最大值=4068.5455)
运行时间:(显示最后3个)
2033.4389 2033.2695 2033.2918(平均值=2030.5825最小值=2017.6880最大值=2035.0352)
测试名称:loop_atomic_weakcas
运行时间:(显示最后3个)
8130.5620 8129.9963 8132.3382(平均值=8114.0052最小值=8042.0742最大值=8132.8542)
运行时间:(显示最后3个)
4066.9559 4067.0414 4067.2080(平均值=4086.0608最小值=4023.6822最大值=4335.1791)
运行时间:(显示最后3个)
2034.6084 2169.8127 2034.5625(平均值=2047.7025最小值=2032.8131最大值=2169.8127)
测试名称:loop_atomic_tlocal_weakcas
运行时间:(显示最后3个)
8132.5267 8132.0299 8132.2415(平均值=8114.9328最小值=8043.3674最大值=8134.0418)
运行时间:(显示最后3个)
4066.5924 4066.5797 4066.6519(平均值=4059.1911最小值=4025.0703最大值=4066.8547)
运行时间:(显示最后3个)
2033.2614 2035.5754 2036.9110(平均值=2033.2958最小值=2023.5082最大值=2038.8750)
虽然上面示例中的线程局部变量可能最终位于相同的缓存线中,但在我看来,普通CAS与其弱版本之间没有明显的性能差异。
这可能意味着,事实上,弱比较和交换充当完全成熟的内存围栏,即充当一个不稳定的变量。
问:这一观察是否正确?另外,是否有一个已知的体系结构或Java发行版的弱比较和集合实际上更快?如果不是,那么首先使用弱CAS有什么好处?