标签:atomic ati oid 相同 如何 image cat atom run
我们先聊聊一个经典面试题:i=i++; 我们知道在虚拟机底层它实际上做了三步:
int temp =i; i = i + 1; i = temp;
i++实际上的操作分为三个部分:读、改、写
我们看看下面的例子:
-
public class TestAtomicDemo {
-
public static void main(String[] args) {
-
AtomicDemo ad = new AtomicDemo();
-
-
for(int i = 0;i<10;i++) {
-
new Thread(ad).start();
-
}
-
}
-
}
-
-
class AtomicDemo implements Runnable{
-
private int serialNumber = 0;
-
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
try {
-
Thread.sleep(200);
-
}catch(InterruptedException e) {
-
-
}
-
-
System.out.println(Thread.currentThread().getName()+":"+getSerialNumber() );
-
}
-
-
public int getSerialNumber() {
-
return serialNumber++;
-
}
-
-
public void setSerialNumber(int serialNumber) {
-
this.serialNumber = serialNumber;
-
}
-
-
-
}
这里很明显,两个数据相同了,当然这不是每次都会发生的。为什么会发生这种情况?
为了方便我们将变量名写成Num,就只画四个线程,根据我们之前说的,num先被线程1读取,然后执行改、写操作,在线程1还没来得及将值写回主存时,线程2读取了主存中的num,此时num依然等于0,这就发生了上面很尴尬的场景了。
我们可以用上一讲谈到的volatile解决吗?当然不行,volatile解决的是可见性,然而我们这里有两步操作,即使是直接在主存操作,依旧可能出现上述情况,即线程1刚刚把num加完正打算写之前,线程2读取了当前num的值,最后依然出现两个1,因为i++存在读改写三步操作,本来这些操作不可分割,但是现在分割执行,破坏了原子性,我们现在需要用到原子变量。
JDK1.5之后,java.util.concurrent.atomic为我们提供了大量的原子变量,这些变量有以下几个特点:
-
含有volatile的特性,原子变量封装的值都是volatile,保证内存的可见性,随便开个源码都可看到:
-
CAS(Compare-And-Swap)算法保证数据原子可见性
CAS算法是硬件对并发操作共享数据的支持
CAS包含了三个操作数:
内存值V 预估值A 更新值B
当且仅当V==A时,V=B,否则将不会做任何操作
那么CAS如何解决原子性问题呢?
当线程1改完值,将主存中的num值修改为1,此时线程2需要进行判断,发现原值与现在值不同,所以什么都不做。这样只会使一个线程修改num,更重要的是CAS算法比普通的同步锁效率高很多,当CAS判定失败,它会放弃对CPU的占用,然后再进行判断。缺点就是我们要手工写一些代码,比如重新判断。
扯了这么多,我们回到原先的题目,使用AtomicInteger来处理原问题的原子性,
-
import java.util.concurrent.atomic.AtomicInteger;
-
-
public class TestAtomicDemo {
-
public static void main(String[] args) {
-
AtomicDemo ad = new AtomicDemo();
-
-
for(int i = 0;i<10;i++) {
-
new Thread(ad).start();
-
}
-
}
-
}
-
-
class AtomicDemo implements Runnable{
-
private AtomicInteger serialNumber = new AtomicInteger(0);
-
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
try {
-
Thread.sleep(200);
-
}catch(InterruptedException e) {
-
-
}
-
-
System.out.println(Thread.currentThread().getName()+":"+getSerialNumber() );
-
}
-
-
public int getSerialNumber() {
-
return serialNumber.getAndIncrement();
-
}
-
}
原子性
标签:atomic ati oid 相同 如何 image cat atom run
原文地址:https://www.cnblogs.com/figsprite/p/10780233.html