作者:毕来生
1、什么是原子变量?
原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题。
2、通过synchronized保证原子操作
3、Atomic之AtomicInteger源码分析
java.util.concurrent.atomic包下帮助我们提供了很多原子性的支持,请参考下图
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位处理器。请忽略以上废话。