Atomic
1. AtomicInteger
a. 多线程并发访问问题
class MyThread extends Thread {
public static volatile int a = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
a++;
}
System.out.println("修改完毕!");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("获取a最终值:" + MyThread.a);
}
}
b. 用 AtomicInteger 类解决
- 用 AtomicInteger 类解决了多线程的原子性问题,无论程序运行多少次,其结果总是正确的;
import java.util.concurrent.atomic.AtomicInteger;
class MyThread extends Thread {
public static AtomicInteger a = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
a.getAndIncrement();
}
System.out.println("修改完毕!");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("获取a最终值:" + MyThread.a.get());
}
}
c. 工作原理:CAS 机制
- 在 Unsafe 类中,调用了一个:compareAndSwapInt() 方法,此方法的几个参数:
var1:传入的 AtomicInteger 对象
var2:AtommicInteger 内部变量的偏移地址
var5:之前取出的 AtomicInteger 中的值
var5 + var4:预期结果
- 此方法使用了一种"比较并交换(Compare And Swap)"的机制,它会用 var1 和 var2 先获取内存中 AtomicInteger 中的值,然后和传入的,之前获取的值 var5 做一下比较,也就是比较当前内存的值和预期的值是否一致,如果一致就修改为 var5 + var4,否则就继续循环,再次获取 AtomicInteger 中的值,再进行比较并交换,直至成功交换为止;
- compareAndSwapInt() 方法是"线程安全"的;
- 我们假设两个线程交替运行的情况,看看它是怎样工作的:
初始 AtomicInteger 的值为0
线程A执行:var5 = this.getIntVolatile(var1,var2);获取的结果为:0
线程A被暂停
线程B执行:var5 = this.getIntVolatile(var1,var2);获取的结果为:0
线程B执行:this.compareAndSwapInt(var1,var2,var5,var5 + var4)
线程B成功将 AtomicInteger 中的值改为1
线程A恢复运行,执行:this.compareAndSwapInt(var1,var2,var5,var5 + var4)
此时线程A使用 var1 和 var2 从 AtomicInteger 中获取的值为:1,而传入的 var5 为0,比较失败,返回 false,继续循环
线程A执行:var5 = this.getIntVolatile(var1,var2);获取的结果为:1
线程A执行:this.compareAndSwapInt(var1,var2,var5,var5 + var4)
此时线程A使用 var1 和 var2 从 AtomicInteger 中获取的值为:1,而传入的var5为1,比较成功,将其修改为 var5 + var4,也就是2,将 AtomicInteger 中的值改为2,结束;
- CAS 机制也被称为乐观锁,因为大部分比较的结果为 true,就直接修改了。只有少部分多线程并发的情况会导致 CAS 失败,而再次循环;
2. AtomicIntegerArray
a. 多线程并发访问问题
class MyThread extends Thread {
private static int[] intArray = new int[1000];
@Override
public void run() {
for (int i = 0; i < getIntArrayLength(); i++) {
intArray[i]++;
}
}
public static int getIntArray(int i) {
return intArray[i];
}
public static int getIntArrayLength() {
return intArray.length;
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new MyThread().start();
}
Thread.sleep(1000 * 5);
System.out.println("主线程休息5秒醒来");
for (int i = 0; i < MyThread.getIntArrayLength(); i++) {
System.out.println(MyThread.getIntArray(i));
}
}
}
b. 用 AtomicIntegerArray 类解决
- AtomicIntegerArray 类可以保证数组的多线程安全;
import java.util.concurrent.atomic.AtomicIntegerArray;
class MyThread extends Thread {
private static int[] intArray = new int[1000];
public static AtomicIntegerArray arr = new AtomicIntegerArray(intArray);
@Override
public void run() {
for (int i = 0; i < arr.length(); i++) {
arr.addAndGet(i, 1);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new MyThread().start();
}
Thread.sleep(1000 * 5);
System.out.println("主线程休息5秒醒来");
for (int i = 0; i < MyThread.arr.length(); i++) {
System.out.println(MyThread.arr.get(i));
}
}
}