Java并发包下AtomicInteger使用及原理

1.AtomicInteger介绍

       AtomicInteger是一个提供原子操作的Integer类,通过线程安全的非阻塞的方式操作加减;在以往java语言中,i++、i--、++i、--i都是线程不安全的,都是先加/减然后在赋值的方式,独立使用并不适合高并发的场景,若想在高并发使用,必须加阻塞关键字synchronized,而这样会对并发性能影响比较大;

2.AtomicInteger简单使用

  具体代码如下  

package com.example.demo.concurrent;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerTest {		    
	   static class MyThread implements Runnable{
		AtomicInteger ato = new AtomicInteger();
		int val = 0;
		@Override
		public void run() {			
			for(int i=0;i<10000;i++){
			   ato.getAndIncrement();
			}
			for(int i=0;i<10000;i++){				
				val++;				
			}
		}
		
		
	}
	   	   
	public static void main(String[] agrs) throws InterruptedException{
		AtomicIntegerTest.MyThread myThread = new AtomicIntegerTest.MyThread();
		Thread thread = new Thread(myThread);
		Thread thread2 = new Thread(myThread);
		thread.start();
		thread2.start();
		Thread.sleep(500);
        System.out.println("AtomicInteger value="+myThread.ato.get()+",val++ value="+myThread.val);
	}
	
	
}

 运行结果:AtomicInteger value=20000,val++ value=11256,明显使用val++因并发导致异常数据,而使用AtomicInteger正常

public void run() {			
for(int i=0;i<10000;i++){
	 ato.getAndIncrement();
}
for(int i=0;i<10000;i++){
	synchronized(this){	//加同步				
		val++;				
	}
}
}

  加同步之后两者结果是一样的  AtomicInteger value=20000,val++ value=20000(后面可以测试下运行性能)

  3.AtomicInteger原理说明

      先看下一段AtomicInteger的源码

       

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

   从头部代码可看出使用了Unsafe,Unsafe是JDK内部的工具类,其中Unsafe.compareAndSwapInt实现cas安全的自增

   定义了全局字段valueOffset,是保存在内存中的偏移量,保存的是当前AtomicInteger的值,并在static静态块中初始化。

  AtomicInteger的真正的值value是一个volatile的类型的变量,volatile是保存在主内存的,是一个所有线程共享可见的变量,不是保存在寄存器(只有拥有线程可见),也就是当某个线程改变volatile修饰符修饰的变量,其他线程都能获得最新的值。

现在再来看看实例中使用的方法

AtomicInteger中有很多方法,例如getAndIncrement() 相当于i++ 和getAndAdd() 相当于i+=n 。从源码中我们可以看出这几种方法的实现很相似,所以我们主要分析getAndIncrement() 方法的源码。

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    /**
     * Atomically decrements by one the current value.
     *
     * @return the previous value
     */
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    /**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //Unsafe类中的代码
    public final int getAndAddInt(Object arg0, long arg1, int arg3) {
	int arg4;
	do {
		arg4 = this.getIntVolatile(arg0, arg1);
	} while (!this.compareAndSwapInt(arg0, arg1, arg4, arg4 + arg3));

	return arg4;
    }

getAndIncrement() 方法实现了自增的操作。核心实现是先获取当前值和目标值(也就是value+1),如果compareAndSwapInt(...) 返回成功则该方法返回目标值。那么compareAndSwapInt是做什么的呢?理解这个方法我们需要

CAS操作理解。

我们学过独占锁和乐观锁的概念。独占锁就是线程获取锁后其他的线程都需要挂起,直到持有独占锁的线程释放锁;乐观锁是先假定没有冲突直接进行操作,如果因为有冲突而失败就重试,直到操作成功。其中乐观锁用到的机制就是CAS,Compare and Swap。另外独占锁典型的操作是数据库for update(synchronized也是独占锁),乐观锁是数据加版本号的方式;

AtomicInteger 中的CAS操作就是compareAndSwapInt(),其作用是每次从内存中根据内存偏移量(valueOffset)取出数据,将取出的值跟期望的值expect 比较,如果数据一致就把内存中的值改为update。

这样使用CAS就保证了原子操作。其余几个方法的原理跟这个相同,在此不再过多的解释。

总结

总结一下,AtomicInteger 中主要实现了整型的原子操作,防止并发情况下出现异常结果,其内部主要依靠JDK 中的unsafe 类操作内存中的数据来实现的。volatile 修饰符保证了value在内存中其他线程可以看到其值得改变。CAS操作保证了AtomicInteger 可以安全的修改value 的值。

    

你可能感兴趣的:(juc)