AtomicInteger关键字

Posted startseven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtomicInteger关键字相关的知识,希望对你有一定的参考价值。

validate 关键字可以保证多线程之间的可见性,但是不能保证原子操作。(需要了解java内存模型jmm)

package com.cn.test.thread;


public class VolatileAutomic extends Thread{
    private volatile static int count = 0;
    public  static void add() {
       for (int i = 0; i < 10000; i++) {
             count ++;
        }
    }
    
    @Override
    public void run() {
        add();
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new VolatileAutomic().start();
        }
        while (Thread.activeCount() > 1) {
            Thread.yield();
        }
    //    System.out.println(atomicCount.get());
        System.out.println("count=" + count);
    }
}

 运行结果:

count=61618

 

    上面例子中volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。

count++是一个非原子性的操作,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致的情况:

假如某个时刻变量count的值为10,线程1对变量进行自增操作,线程1先读取了变量count的原始值,然后线程1被阻塞了;线程2也对变量进行自增操作,线程2先读取了count的原始值,线程1只是进行了读取操作,没有进行写的操作,所以不会导致线程2中的本地缓存无效,因此线程2进行++操作,在把结果刷新到主存中去,此时线程1在还是读取原来的10 的值在进行++操作,所以线程1和线程2对于count=10进行两次++操作,结果都为11.。

  上述问题解决方法:

                            1.采用add方法中加入sychnorized.,或者同步代码块

                            2.采用AtomicInteger

package com.cn.test.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileAutomic extends Thread{
    static AtomicInteger atomicCount = new AtomicInteger(0);
    public  static void add() {
        for (int i = 0; i < 10000; i++) {
            atomicCount.incrementAndGet();
        }
    }
    
    @Override
    public void run() {
        add();
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new VolatileAutomic().start();
        }
        while (Thread.activeCount() > 1) {
            Thread.yield();
        }
        System.out.println(atomicCount.get());
    }
}

运行结果:

100000

AtomicInteger是java.util.concurrent.atomic并发包下面一个类。

AtomicInteger源码理解:

static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
  /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

调用AtomicInteger的构造方法传入一个initialValue值,将initialValue复制给一个volatile 的value,

 /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
public final int getAndAddInt(Object var1, long var2, int var4) {
		int var5;
		do {
			var5 = this.getIntVolatile(var1, var2);
		} while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

		return var5;
	}

底层源码实现主要采用cas算法的思想,通过操作底层硬件的指令。cas锁是一种无锁的机制。cas的机制:v的值应该是A,如果是A则将值更新为B。当多个线程同时要更新V的值,

只有其中一个线程更新成功,其他线程更新失败,更新失败的线程并不会挂起,而是在继续进行竞争资源,对V的值进行操作。

cas锁的机制采用的是事物提交-- 失败--重试来保证原子性。

cas锁的缺点:当并发量大的时候,多线程一直进行重试,这样会导致CPU的开销很大。 

以上是关于AtomicInteger关键字的主要内容,如果未能解决你的问题,请参考以下文章

AtomicInteger在实际项目中的应用

AtomicInteger关键字

Java AtomicInteger的用法

JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用

java中关于AtomicInteger的使用

详解java并发原子类AtomicInteger(基于jdk1.8源码分析)