In chapter 1, volatile does not guarantee atomicity. AtomicInteger is used to solve the problem of atomicity, and we use AtomicInteger to solve the problem of atomicity. Use AtomicReference atomic references
AtomicInteger AtomicInteger:
package com.javaliao.backstage;
import java.util.concurrent.atomic.AtomicInteger;
class MyData{
volatile int number = 0;
AtomicInteger atomicInteger = new AtomicInteger();
public void changeData(){
atomicInteger.getAndIncrement();//加一
}
}
/**
* 线程对变量的读取赋值要先将变量从主内存拷贝自己的工作内存空间,在工作内存中进行操作,操作完成后再将变量写回主内存
*/
public class Demo {
//主线程main,程序入口
public static void main(String[] args) {
//创建对象,number在主内存为0
MyData myData = new MyData();
for (int i = 1; i <= 20; i++) {
//创建20个线程
new Thread(()->{
//一个线程执行1000次加一的操作
for (int j = 1; j <= 1000; j++) {
myData.changeData();
}
},String.valueOf(i)).start();
}
//程序不关闭会继续执行main线程和GC线程,判断线程数量大于二继续执行上面的代码,
while (Thread.activeCount() > 2){
Thread.yield();
}
//理想中number的数量为20*1000=20000,而volatile不保证原子性,实际情况一般打印number的数量不是20000
System.out.println(Thread.currentThread().getName()+"\t 打印number的数量:" + myData.atomicInteger);
}
}
Copy the code
AtomicReference code directly on the atom reference:
package com.javaliao.backstage; import java.util.concurrent.atomic.AtomicReference; class User{ String userName; int age; public User(String userName, int age) { this.userName = userName; this.age = age; } @Override public String toString() { return "User{" + "userName='" + userName + '\'' + ", age=" + age + '}'; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }} public class Demo {public static void main(String[] args) {User user1 = new User("java_wxid",25); User user2 = new User("javaliao",22); AtomicReference<User> atomicReference = new AtomicReference<>(); atomicReference.set(user1); System.out.println(atomicReference.compareAndSet(user1, user2)+"\t"+atomicReference.get().toString()); new Thread(()->{ System.out.println(atomicReference.compareAndSet(user1, user1)+"\t"+atomicReference.get().toString()); },"a").start(); }}Copy the code
Console:
However, this does not solve the ABA problem of CAS explained in the previous chapter
ABA Question code:
public class Demo { static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); Public static void main (String [] args) {new Thread (() - > {/ / ABA operation on atomicReference.com pareAndSet (100101); AtomicReference.com pareAndSet (101100); },"t1").start(); Thread.sleep(1000); new Thread(()->{try {// Pause for a second to ensure that the T1 Thread completes an ABA operation. } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(100, 2019)); System.out.println(atomicReference.get()); },"t2").start(); }}Copy the code
The previous chapter explained that there was a catch, so here’s the solution:
Use an AtomicStampedReference version number atomic reference
As long as the T1 version number, weak threads in the T2 version number needs to be updated, assuming that thread T1 version number of the second value is 2019, and thread T2 have changed two times, the version number is 3, that at this point that thread T2 version number is not the comparison and exchange of 2, you need to copy the thread version number of the values of T3 update again.
package com.javaliao.backstage; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; Public class Demo {static AtomicStampedReference<Integer> AtomicStampedReference = new AtomicStampedReference<>(100,1); Public static void main (String [] args) {System. Out. Println (" = = = = = = = = = = = = = = = solve the problem of ABA solution = = = = = = = = = = = = = = = "); New Thread (() - > {/ / get the version number int stamp. = atomicStampedReference getStamp (); System.out.println(thread.currentThread ().getName()+" stamp+"\t "+atomicStampedReference.getReference()); Try {// Pause thread. sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.com pareAndSet (100101, atomicStampedReference getStamp (), atomicStampedReference. GetStamp () + 1); System. Out.println (Thread. CurrentThread (). The getName () + "\ t second version:" + atomicStampedReference. GetStamp () + "\ t the current actual value: latest "+atomicStampedReference.getReference()); AtomicStampedReference.com pareAndSet (101100, atomicStampedReference getStamp (), atomicStampedReference. GetStamp () + 1); System. Out.println (Thread. CurrentThread (). The getName () + "\ t version number for the third time:" + atomicStampedReference. GetStamp () + "\ t the current actual value: latest "+atomicStampedReference.getReference()); },"t3").start(); New Thread (() - > {/ / get the version number int stamp. = atomicStampedReference getStamp (); System.out.println(thread.currentThread ().getName()+"\t "; Try {// Pause for a second to ensure that t3 has completed an ABA operation thread. sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System. Out.println (Thread. CurrentThread (). The getName () + "\ t the latest version:" + atomicStampedReference. GetStamp () + "\ t current t4 version number is: "+stamp); System.out.println(thread.currentThread ().getName()+"\t Can be written back to the main memory only when the latest version is the same as the t4 version. "+ atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1)); System. The out. Println (Thread. CurrentThread (). The getName () + "\ t the current actual value: latest" + atomicStampedReference. GetReference ()); },"t4").start(); }}Copy the code
Console:
At this point, the T4 thread can be told to update the value of version 3, 100, which solves the problem that CAS does not care about the process.