一、原子变量
1.多线程++操作由于非原子变量导致问题
(1)预期
10个线程访问内部变量serialNumber,并进行serialNumber++操作。理论上serialNumber应当直接从0到10,但是结果和预期不符。
(2)代码
package thread.threadpool;
public class AtomicDemo {
public static void main(String[] args) {
AtomicTest at=new AtomicTest();
for (int i = 0; i < 10; i++) {
new Thread(at).start();
}
}
}
class AtomicTest implements Runnable{
private int serialNumber;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+getSerialNumber());
}
public int getSerialNumber() {
return serialNumber++;
}
}
结果:出现了3个0,而且最终到达7,而不是10.
Thread-4:0
Thread-8:0
Thread-9:7
Thread-0:2
Thread-7:1
Thread-3:3
Thread-1:4
Thread-2:0
Thread-6:6
Thread-5:5
2.导致问题原因
(1)serialNumber ++操作步骤分三步:“读-改-写“
int temp= serialNumber; //从主内存中读取
serialNumber = serialNumber+1;//修改,然后,写入主内存
(2)两个线程同时读取,并且修改可能会同时读取同一个值。
然后写入的是针对读取的同样的值进行处理的结果。
3.解决问题方法:采用原子变量
(1)原子变量包:package java.util.concurrent.atomic;
(2)原子变量特性:所有原子变量都具有内存可见性(被volatile关键字修饰)。同时,具有原子性(通过CAS算法保证)
二、CAS(Compare And Swap算法)
1.CAS是硬件对于并发操作共享数据的支持。
2.CAS操作
(1)包含三个变量:主内存值V,预估值A,更新值B
(2)首先,读取主内存值V。
然后,在操作该变量后,准备重新写入的时候,重新读取一下主内存中值A,比较A和V。
最后,当且仅当V==A的时候,把B赋值给V,否则不进行任何操作。
3.CAS解决原子性问题
上图的原子性问题,可以通过CAS解决如下
4.具体代码:原子变量AtomicInteger
package thread.threadpool;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicDemo {
public static void main(String[] args) {
AtomicTest at=new AtomicTest();
for (int i = 0; i < 10; i++) {
new Thread(at).start();
}
}
}
class AtomicTest implements Runnable{
private AtomicInteger serialNumber;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+getSerialNumber());
}
public int getSerialNumber() {
return serialNumber.getAndIncrement();
}
}
结果:
Thread-5:0
Thread-1:3
Thread-0:2
Thread-4:1
Thread-2:4
Thread-3:7
Thread-8:6
Thread-9:5
Thread-6:8
Thread-7:9