原子更新基本类型
private static ExecutorService fixedThreadPool= Executors.newFixedThreadPool(10);
private static AtomicInteger atomicInteger=new AtomicInteger(0);
@Test
public void test01(){
for (int i = 0; i < 10; i++) {
fixedThreadPool.submit(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
atomicInteger.getAndIncrement();
}
}
});
}
fixedThreadPool.shutdown();
while (true) {
if (fixedThreadPool.isTerminated()) {
break;
}
}
System.out.println("run over atomicInteger="+atomicInteger.get());
}
原子更新数组
private static int[] value=new int[]{1,2,3};
private static AtomicIntegerArray atomicIntegerArray=new AtomicIntegerArray(value);
/**
* 原子更新数组
*/
@Test
public void test01(){
int newValue = atomicIntegerArray.addAndGet(0, 3);
System.out.println("newValue:"+newValue);
}
原子更新引用
private static AtomicReference<User> userAtomicReference = new AtomicReference<>();
/**
* 原子更新引用类型
*/
@Test
public void test03() {
User user01 = new User(1, "user01");
userAtomicReference.set(user01);
User user02 = new User(2, "user02");
userAtomicReference.compareAndSet(user01, user02);
System.out.println(userAtomicReference.get().getUserName());
}
private static class User {
private Integer id;
private String userName;
public User(Integer id, String userName) {
this.id = id;
this.userName = userName;
}
//get set
}
原子更新字段
private static class User04{
public volatile int id;
private String userName;
public User04(int id, String userName) {
this.id = id;
this.userName = userName;
}
//get set
}
private static AtomicIntegerFieldUpdater<User04> atomicIntegerFieldUpdater=AtomicIntegerFieldUpdater
.newUpdater(User04.class,"id");
/**
* 原子更新字段
*/
@Test
public void test04(){
User04 user04=new User04(1,"user01");
atomicIntegerFieldUpdater.getAndIncrement(user04);
System.out.println(atomicIntegerFieldUpdater.get(user04));
}
原子更新最核心的流程
获取A变量内存偏移量(即在主内存中的地址),传递给Unsafe中的getAndAddInt。
1 getAndAddInt中先通过内存偏移量从主内存中获取数据a,
2 然后将a传递给compareAndSwapInt,compareAndSwapInt将a和内存中的值进行比较,如果相同则将新的值赋给A变量,如果不相同,则继续循环执行1步骤。
compareAndSwapInt能保证获取值,进行比较,然后赋值是连续的、原子的、不可中断.
CAS
比较当前工作内存中的值和主内存中的值,如果相同则执行规则,问题的关键是比较内存中的值然后执行交换,要保证原子性与连续性、不可中断性。
CAS的缺陷
1 循环时间长,如果CAS失败,会一直进行尝试,如果CAS长时间不成功,会对CPU带来很大的开销。
2 只能保证一个共享变量的原子操作。
3 引入了ABA问题。
//------------------ABA问题演示---------------------
private static AtomicReference<Integer> integerAtomicReference=new AtomicReference<>(100);
@Test
public void test05(){
new Thread(new Runnable() {
@Override
public void run() {
integerAtomicReference.compareAndSet(100,101);
integerAtomicReference.compareAndSet(101,100);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行成功否:"+integerAtomicReference.compareAndSet(100,101)+" 结果:"+integerAtomicReference.get());
}
}).start();
while (Thread.activeCount()>2){
}
}
线程x从主内存中获取到共享变量z的值A到本地内存中,然后时间片完了,
线程y从主内存获取z的值A,然后更新z的值为B,刷新到主内存中,但是有将z的值改为了A,这样对于A来说相当于没有改动过。
线程x执行CAS执行成功。
解决方案–AtomicStampedReference的引用加版本号
//---------------------------- ABA问题解决方式AtomicStampedReference--------------------------------
private static AtomicStampedReference<Integer> integerAtomicStampedReference=new AtomicStampedReference<>(1,1);
@Test
public void test06(){
new Thread(new Runnable() {
@Override
public void run() {
int stamp = integerAtomicStampedReference.getStamp();
System.out.println("stamp:"+stamp);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
integerAtomicStampedReference.compareAndSet(1,2,1,++stamp);
integerAtomicStampedReference.compareAndSet(2,1,2,++stamp);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
int stamp = integerAtomicStampedReference.getStamp();
System.out.println("stamp:"+stamp);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean compareResult = integerAtomicStampedReference.compareAndSet(1, 2, stamp, stamp+1);
System.out.println("compareResult:"+compareResult);
System.out.println("stamp"+integerAtomicStampedReference.getStamp());
}
}).start();
while (Thread.activeCount()>2){
}
}
摘自《Java多线程并发编程艺术》