什么是原子操作类
当更新一个变量的时候,多出现数据争用的时候可能出现所意想不到的情况。这时的一般策略是使用synchronized解决,因为synchronized能够保证多个线程不会同时更新该变量。然而,从jdk 5之后,提供了粒度更细、量级更轻,并且在多核处理器具有高性能的原子操作类。因为原子操作类把竞争的范围缩小到单个变量上,这可以算是粒度最细的情况了。
原子操作类相当于泛化的volatile变量,能够支持原子读取-修改-写操作。比如AtomicInteger表示一个int类型的数值,提供了get和set方法,这些volatile类型的变量在读取与写入上有着相同的内存语义。原子操作类共有13个类,在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。
下面介绍这各种原子操作类
1 原子更新基本类型:使用原子方式更新基本类型
AtomicBoolean:原子更新布尔变量
AtomicInteger:原子更新整型变量
AtomicLong:原子更新长整型变量
2 原子更新数组:通过原子更新数组里的某个元素
AtomicIntegerArray:原子更新整型数组的某个元素
AtomicLongArray:原子更新长整型数组的某个元素
AtomicReferenceArray:原子更新引用类型数组的某个元素
3 原子更新引用类型:更新引用类型
AtomicReference:原子更新引用类型
AtomicReferenceFieldUpdater:原子更新引用类型里的字段
AtomicMarkableReference:原子更新带有标记位的引用类型
4 原子更新字段:原子更新某个类的某个字段
AtomicIntegerFieldUpdater:原子更新整型字段
AtomicLongFieldUpdater:原子更新长整型字段
AtomicStampedReference:原子更新带有版本号的引用类型
我们接下来会通过对AtomicInteger的讲解来深入认识原子操作类
在单线程环境下,执行num++等操作时不会出现问题,那么,在多线程环境下是否还是跟单线程环境下一样安全呢?我们通过一段代码进行模拟。
public class Demo {
private static int num = 0;
public static void add() {
num++;
}
public static void main(String[] args) {
Thread[] myThread = new Thread[10];
for(Thread thread : myThread) {
thread = new Thread() {
@Override
public void run() {
for(int i = 0;i < 1000;i++) {
add();
}
}
};
thread.start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(num);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
我们进行三次测试,得出结果分别是
8400
8318
9856
在没有同步条件下,自增操作不具备原子性,在多线程环境下是不安全的。
既然我们上面的方法不安全,那该咋办呢,总不能坐以待毙吧?没事,我们有一件新武器,那就是AtomicInteger原子类型。把上面的代码改为使用AtomicInteger,让我们看看接下来会发生什么?
import java.util.concurrent.atomic.AtomicInteger;
public class Demo {
public static AtomicInteger num = new AtomicInteger(0);
public static void add() {
num.incrementAndGet();
}
public static void main(String[] args) {
Thread[] myThread = new Thread[10];
for(Thread thread : myThread) {
thread = new Thread() {
@Override
public void run() {
for(int i = 0;i < 1000;i++) {
add();
}
}
};
thread.start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(num);
}
}
---------------------