验证volatile不具备原子性
测试
public class VolatileAtomicTest {
public static volatile int COUNT = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 500; i++) {
new Thread(() -> {
try {
Thread.sleep(20);
//为了达到并行的效果 已经生成的线程等待10ms的 等待其他线程生成
} catch (InterruptedException e) {
e.printStackTrace();
}
COUNT++;
}).start();
}
Thread.sleep(1000);
System.out.println("执行结果" + COUNT);
}
}
COUNT已经使用volatile修饰
保证了对COUNT进行的操作的有序性和可见性
此处结果不正确是因为线程争抢破坏了自增操作的原子性
自增操作实际分为多个步骤
- 取操作数
- 操作压入线程的栈顶
- 线程弹栈
- 操作数+1
- 操作数压入线程的栈顶
- 弹栈完成赋值
volatile只能保证以上操作造成的修改对其他线程可见
即对volatiole的操作 从主存取操作数 操作结果及时刷入主存)
及以上操作的有序性
即对 被volatiole修饰的值 的操作不会被指令重排
但不能保证以上操作不被分割中断
如一种可能的情况
- 线程1在执行到步骤2时因为时间片切换进入从RUNING状态 切换到RUNNABLE状态
此时其栈内的操作数值为0 - 线程2执行第1 2步 后时间片切换进入RUNNABLE状态
此时其栈内操作数值也为0 - 线程1执行3 4 5 6步
写入主存的赋值数值为1 - 线程2 执行3 4 5 6步
写入主存的赋值数值也为1
两次在不同线程的执行的自增操作实际只让主存中的COUNT值增加了1
使用同步解决原子性问题
使用sychronized或lock等方式同步多个线程对COUNT
可以保证在下一个线程操作COUNT时
上一个线程对COUNT的操作已经完成
可以保证对COUNT的操作指令不被分割执行
也就保证了操作的原子性