原子变量和CAS算法
什么是原子性?
不可分割的操作,要么全部做了,要么一点也不做
i++
的原子性问题
/**
* 实际上i++分为三个步骤 “读-改-写”
* int i = 0;
* i++;
* 实际上的操作:
* int tmp = i;
* tmp = i+1;
* i = tmp;
*/
所以,i++
操作并不是原子性的
看下面的一段代码:
public class Main {
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;
public int getSerialNumber()
{
return serialNumber++;
}
@Override
public void run() {
try{
Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+" : "+getSerialNumber());
}
catch (Exception e )
{
}
}
}
代码逻辑:
- 开10个线程,每个线程都对
serialNumber
做加一操作
实际上运行结果:
Thread-6 : 2
Thread-8 : 0
Thread-1 : 7
Thread-2 : 6
Thread-7 : 1
Thread-3 : 5
Thread-0 : 8
Thread-9 : 0
Thread-5 : 3
Thread-4 : 4
注意:由于线程并发冲突并不是每次都发生的,发生后产生的结果也不尽相同
- 可以看到结果出现了问题(出现了两个0,没到到达9)
发生了什么?
- 多个线程同时访问主存中的共享数据
- 线程1拿到
serialNumber
加一打印,同时线程2 也拿到serialNumber
并加一打印 - 造成了加一结果重复
- 即使用
volatile
关键字,也没办法保证原子性问题
- 线程1拿到
怎么解决?
一、使用原子变量
可以参见java.util.concurrent.atomic
包中提供的一些保证原子性的类(jdk1.5之后提供)
- 用
volatile
保证内存可见性 - 用
CAS
算法保证数据的原子性,CAS是硬件对并发操作共享数据的支持
什么是CAS?
Java中CAS的过程:
-
内存位置(Java中简单理解为变量内存地址,用V表示)
-
旧的预期值A,准备设置的新值B
-
当且仅当V符合A时,处理器才会用B更新V,否则不执行更新,最终无论是否更新都返回V的地址
这种算法就保证了修改操作的原子性
为什么比锁效率高?
即使CAS操作不成功,也不会阻塞,而是再次尝试,直到成功
注意:如果尝试的次数过多,CAS操作的效率可能还不如锁,这时候一些轻量级锁就会进行粗化操作,详细的说明可以去看JVM原理相关资料
将代码中的int
类变为AtomicInteger
类,如下:
import java.util.concurrent.atomic.*;
public class Main {
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();
public int getSerialNumber()
{
//获取并递增
return serialNumber.getAndIncrement();
}
@Override
public void run() {
try{
Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+" : "+getSerialNumber());
}
catch (Exception e )
{
}
}
}
执行结果:
Thread-4 : 2
Thread-8 : 7
Thread-7 : 8
Thread-3 : 3
Thread-9 : 6
Thread-5 : 4
Thread-6 : 5
Thread-2 : 1
Thread-1 : 0
Thread-0 : 9
从0-9,结果正确