共计 2909 个字符,预计需要花费 8 分钟才能阅读完成。
作者:毕来生
微信:878799579
1、什么是原子变量?
原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题。
2、通过 synchronized 保证原子操作
- 获取锁对象
- 获取失败 / 获取不到 -> 阻塞队列等待
- 释放锁对象
3、Atomic 之 AtomicInteger 源码分析
java.util.concurrent.atomic 包下帮助我们提供了很多原子性的支持,请参考下图
- AtomicInteger 和 AtomicIntegerArray:基于 Integer 类型
- AtomicBoolean:基于 Boolean 类型
- AtomicLong 和 AtomicLongArray:基于 Long 类型
-
AtomicReference 和 AtomicReferenceArray:基于引用类型
构造方法如下
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {value = initialValue;}
/**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {}
如果通过构造方法设置原子变量。如不设置初始值则会默认初始化为 0。
以下为方法为 AtomicInteger 基于原子操作常用方法
// 获取当前原子变量中的值并为其设置新值
public final int getAndSet(int newValue)
// 比较当前的 value 是否等于 expect,如果是设置为 update 并返回 true,否则返回 false
public final boolean compareAndSet(int expect, int update)
// 获取当前的 value 值并自增一
public final int getAndIncrement()
// 获取当前的 value 值并自减一
public final int getAndDecrement()
// 获取当前的 value 值并为 value 加上 delta
public final int getAndAdd(int delta)
4、实战演练
在多线程下。我希望对 num = 0;进行自增,10 个线程,每个线程对变量自增 10000 次。结果应该是 100000 才对。
首先我们先来一个错误示范:
package org.bilaisheng.juc;
/**
* @Author: bilaisheng
* @Wechat: 878799579
* @Date: 2019/1/1 21:58
* @Todo: AtomicInteger 原子性错误示范。仅演示使用
* @Version : JDK11 , IDEA2018
*/
public class AtomicIntegerErrorTest {
// 举例 10 条线程并发,实际条数请参考自己场景
private static final int THREAD_COUNT = 10;
private static int num = 0;
public static void main(String[] args) {Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {threads[i] = new Thread(new Runnable() {
@Override
public void run() {
// 此处设置 10000. 太小看不到效果。请酌情设置
for (int j = 1; j <=10000 ; j++) {
// 如想要看到结果请放开下行注释
//System.out.println(Thread.currentThread().getName() +"num ="+num);
num++ ;
}
}
});
threads[i].start();}
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.activeCount());
while (Thread.activeCount()>2){Thread.yield();
}
System.out.println(num);
}
}
运行结果举例两张如下图所示。每次运行结果都不相同
接下来我们的 AtomicInteger 就该登场了
package org.bilaisheng.juc;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author: bilaisheng
* @Wechat: 878799579
* @Date: 2019/1/1 23:02
* @Todo:
* @Version : JDK11 , IDEA2018
*/
public class AtomicIntegerTest {
private static final int THREADS_CONUT = 10;
public static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) {Thread[] threads = new Thread[THREADS_CONUT];
for (int i = 0; i < threads.length; i++) {threads[i] = new Thread(new Runnable() {
@Override
public void run() {
// 此处设置 10000. 太小看不到效果。请酌情设置
for (int j = 1; j <=10000 ; j++) {
// 如想要看到结果请放开下行注释
//System.out.println(Thread.currentThread().getName() +"num ="+num);
num.incrementAndGet();}
}
});
threads[i].start();}
while (Thread.activeCount() > 2) {Thread.yield();
}
System.out.println(num);
}
}
结果是不管怎么运行结果都和预期下相同。运行结果如下图:
5、Volatile 可以保证变量原子性吗?
我们先来看一下下面两行代码
private volatile long num = 4642761357212574643L;
private volatile double amt= 4642761357212574643.23165421354;
如上代码并不能保证 num 以及 amt 两个变量就是原子性的,在 32 位处理器中 哪怕是通过 volatile 关键字进行修饰也无法保证变量原子性。因为在 32 位系统下。如果出现了多线程同时操作某个变量。这个变量正在被线程 a 进行修改。此时线程 b 访问此变量。可能只获取到该变量的前 32 位,故可能会导致原子性失效。导致不可预知的情况以及错误。如果本地和生产均为 64 位处理器。请忽略以上废话。
正文完
发表至: java
2019-07-26