Java数组是线程安全的吗?无同步双线程操作数组结果正确是巧合吗?
问题解答
你多次运行得到相同结果只是当前测试场景下的巧合,不存在特殊底层机制保障结果正确,这段代码本质是线程不安全的。
为什么当前测试结果固定
你的测试用例逻辑非常简单,单线程跑完整个循环的耗时极短,几乎必然出现第一个启动的线程在第二个线程开始执行循环前就已经完成了所有位置的交换操作。此时第二个线程遍历数组时,所有位置已经满足a[i] <= b[i]的条件,不会做任何修改,所以最终结果和单线程执行一次交换的结果完全一致,也就是你看到的固定输出。
代码存在的线程安全隐患
1. 复合操作不具备原子性
对单个下标的判断、交换是多个独立的CPU指令,没有同步保护的情况下,两个线程可以同时操作同一个下标i的元素,很容易出现逻辑异常。举个简单的时序例子:
以i=0的初始状态
a[0]=9、b[0]=0为例:
- 线程1判断
a[0]>b[0]成立,将b[0]的值0存入自己的临时变量temp- 此时线程调度切换到线程2,线程2也判断
a[0]>b[0]成立,也将b[0]的值0存入自己的临时变量temp- 切回线程1,执行
b[0] = a[0](b[0]赋值为9),再执行a[0] = temp(a[0]赋值为0),此时位置0的值符合预期:a[0]=0、b[0]=9- 再切回线程2,继续执行未完成的逻辑:先执行
b[0] = a[0](此时a[0]已经是0,所以b[0]被错误覆盖为0),再执行a[0] = temp(temp是0,a[0]保持0不变),最终位置0变成a[0]=0、b[0]=0,和预期结果不符。
2. 共享变量可见性无保障
Java内存模型没有规定无同步保护的共享变量修改必须对其他线程立即可见,数组元素没有volatile修饰,也没有加锁同步,极端情况下第二个线程可能完全看不到第一个线程对数组的修改,重复执行交换操作,最终结果完全不可控。
验证方法
你可以在run方法的for循环内加入短时间休眠,放大线程交替执行的概率,运行几次就能看到不符合预期的结果,甚至出现a[i] > b[i]的逻辑错误。
内容的提问来源于stack exchange,提问作者user13121591
相关产品推荐
相关产品推荐

