版权声明:如果您觉得此文有用或对您有帮助,请不吝点个赞或留个言,哈哈! https://blog.csdn.net/fanrenxiang/article/details/80623884
为什么需要AtomicInteger原子操作类?
对于全局变量的数值类型操作 num++,若没有加synchronized关键字则是线程不安全的,num++解析为num=num+1,明显,这个操作不具备原子性,多线程时必然会出现问题。测试下:
-
public
class AtomicIntegerTest1 {
-
public
static
int count =
0;
-
-
public static void main(String[] args) {
-
for (
int i =
0; i <
10000; i++) {
-
new Thread() {
-
public void run() {
-
count++;
-
}
-
}.start();
-
}
-
System.out.println(
"count: " + count);
-
}
-
-
}
输出的结果为count: 9992,这个值不定,每次测试都可能不一样,很显然,100个线程跑++操作,结果并没有像预期的那样count: 10000。
要是换成volatile修饰count变量呢?
volatile修饰的变量能够在线程间保持可见性,能被多个线程同时读但是又能保证只被单线程写,并且不会读取到过期值(由java内存模型中的happen-before原则决定的)volatile修饰字段的写入操作总是优先于读操作,即使多个线程同时修改volatile变量字段,总能保证获取到最新的值。试试
-
public
class AtomicIntegerTest3 {
-
static
volatile
int count =
0;
-
-
public static void main(String[] args) throws InterruptedException {
-
for (
int i =
0; i <
100; i++) {
-
new Thread() {
-
public void run() {
-
for (
int j =
0; j <
100; j++) {
-
count++;
-
}
-
}
-
}.start();
-
}
-
Thread.sleep(
1000);
-
System.out.println(
"volatile count: " + count);
-
}
-
}
结果似乎又失望了,试了大约10次后出现volatile count: 9984,果然还是出现问题了,volatile仅仅保证变量在线程间保持可见性,却依然不能保证非原子性的操作。
用了AtomicInteger类后会变成什么样子呢?
把上面的代码改造成AtomicInteger原子类型,看看效果
-
public
class AtomicIntegerTest2 {
-
-
public
static AtomicInteger count =
new AtomicInteger(
0);
-
-
public static void main(String[] args) throws InterruptedException {
-
for (
int i =
0; i <
100; i++) {
-
new Thread() {
-
public void run() {
-
for (
int j =
0; j <
100; j++) {
-
count.getAndIncrement();
-
}
-
}
-
}.start();
-
}
-
Thread.sleep(
1000);
-
System.out.println(
"AtomicInteger count: " + count);
-
}
-
}
结果每次都输出"AtomicInteger count: 10000",没毛病。concurrent(我这里是jdk1.7)包下提供了12种原子操作类型,如下:
原子更新基本类型
atomic包下提供了AtomicBoolean/AtomicLong/AtomicInteger三个原子更新基本类型,以AtomicInteger为例,其他两种基本类似。以下是AtomicInteger囊括的大致方法
-
public final int getAndSet(int newValue) //给AtomicInteger设置newValue并返回加oldValue
-
public
final
boolean
compareAndSet
(int expect, int update)
//如果输入的值和期望值相等就set并返回true/false
-
public
final
int
getAndIncrement
()
//对AtomicInteger原子的加1并返回当前自增前的value
-
public
final
int
getAndDecrement
()
//对AtomicInteger原子的减1并返回自减之前的的value
-
public
final
int
getAndAdd
(int delta)
//对AtomicInteger原子的加上delta值并返加之前的value
-
public
final
int
incrementAndGet
()
//对AtomicInteger原子的加1并返回加1后的值
-
public
final
int
decrementAndGet
()
//对AtomicInteger原子的减1并返回减1后的值
-
public
final
int
addAndGet
(int delta)
//给AtomicInteger原子的加上指定的delta值并返回加后的值
以getAndIncrement为例看下源码
-
public final int getAndIncrement() {
-
for (;;) {
-
//先取出AtomicInteger的当前值
-
int current = get();
-
//对当前值加1操作
-
int next = current +
1;
-
//这里很关键,通过compareAndSet方法比较当前值有没有被其它线程修改过,若修改过返回false则再次进入compareAndSet方法判断
-
if (compareAndSet(current, next))
-
return current;
-
}
-
}
compareAndSet方法里面是调用了Unsafe类的compareAndSwapInt方法
-
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
-
-
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
-
-
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
Unsafe是Java HotSpot提供的操作内存和线程的"后门",官方或者对于生产环境并不建议使用Unsafe类,因为它的API不稳定、不安全,错误使用将给你的HotSpot jvm带来致命性的灾难。同时对于其他基本类型,比如char、float、double等并没有对应的上述判断是否被修改方法,故可以将其转为compareAndSwapInt来简介判断,因为在AtomicBoolean的源码中就是这么做的。
相应的,concurrent包下除了提供的原子更新基本类型,还有原子更新数据、原子更新引用类型、原子更新字段类,最常用的也就是原子更新基本类型了。