1.i++和++i问题
@Test
public void test1(){
int i = 0,j,k;
j = i++;
k = ++j;
System.out.println("i="+i+",j="+j+",k="+k);
}
问题分析:
如上图,以i++操作为例,显然该操作是非原子性的,在底层实际上可分为“读-改-写”三个步骤。试想若有多个线程同时对某个共享变量i进行i++操作,会出现什么结果呢???
2.多线程情况下的i++操作
同时开启10个线程,对共享变量i进行i++操作并打印
class TestDemo implements Runnable{
private int i = 0;
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(getI()+" ");
}
public int getI() {
return i++;
}
}
public static void main(String[] args){
TestDemo td = new TestDemo();
for (int i = 0; i < 10; i++) {
new Thread(td).start();
}
}
结果:
是因为内存可见性问题吗?经过试验,对共享变量用volatile修饰,依然存在重复值问题!!!
问题分析:
虽然volatile保证了内存可见性,但是由于i++操作是非原子操作,可能导致两个线程同时取到相同的值,并都进行了++操作,导致第二次打印两个相同的值。
在jdk1.5后,java.util.concurrent.atomic包下提供了常用的原子变量来解决这一问题。
↓↓↓↓
3.原子变量
首先试试刚才的例子,结果与预期相同,并未出现重复值。
class TestDemo1 implements Runnable{
private AtomicInteger i = new AtomicInteger();
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(getI()+" ");
}
public int getI() {
return i.getAndAdd(1);
}
}
1)基本的原子变量
2)常用方法(AtomicInteger)
其他原子类方法查阅API
4.CAS算法
原子变量可以解决上面问题的两个方面原因:
-
volatile保证内存可见性
-
CAS(Compare-And-Swap)算法保证数据原子性
CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。
CAS是一种无锁的非阻塞算法实现
原理:
CAS包含三个操作数:
- 需要读写的内存值 V
- 预期值 A
- 拟写入的新值 B
只有当需要读入的内存值V和预期值A相同时,才会将值B写入内存。如果两个线程同时进行i++操作,它们同时读取到内存值0,当线程1对内存的值更新成功,线程2的v值已经变为1导致V!=A更新失败。
注:菜鸟一枚,才疏学浅,希望各位前辈能够批评指正,感谢感谢!!!