原子性 CAS算法
一、 i++ 的原子性问题
1、问题的引入:
i++ 的实际操作分为三个步骤:读--改--写
实现线程,代码如下:
public class AtomicDemo implements Runnable
{
private int serialNumber = 0;
@Override
public void run()
{
try
{
Thread.sleep(400);
}
catch (InterruptedException e)
{}
System.out.println(Thread.currentThread().getName() + ": " + this.getSerialNumber());
}
public int getSerialNumber()
{
return serialNumber++;
}
}
测试类,代码如下:
public class AtomicTest
{
public static void main(String[] args)
{
AtomicDemo atomicDemo = new AtomicDemo();
for (int i = 0; i < 10; i++)
{
new Thread(atomicDemo).start();
}
}
}
结果如下图所示:
以上的原子性问题可以使用“原子变量”解决;
二、原子变量
在JDK1.5 之后,提供了 java.util.concurrent.atomic 包下提供了常用的原子变量;
AtomicBoolean、AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpdater、AtomicLong、AtomicReference 等
从以上类源码中可以看出:
(1)变量使用了 Volatile 修饰,保证了内存可见性;
(2)使用了 CAS(Compare-And-Swap)算法,保证了数据的原子性;
CAS算法是硬件对于并发操作共享数据的支持;
CAS 包含了三个操作数:内存值 、预估值 、更新值 ;
当且仅当内存值等于预估值时,把更新值赋给内存值,否则将不做任何操作;
class AtomicDemo implements Runnable
{
private AtomicInteger serialNumber = new AtomicInteger();
@Override
public void run()
{
try
{
Thread.sleep(400);
}
catch (InterruptedException e)
{}
System.out.println(Thread.currentThread().getName() + ": " + this.getSerialNumber());
}
public int getSerialNumber()
{
return serialNumber.getAndIncrement();
}
}