java多线程并发-原子性操作类的应用

在java5以后,我们接触到了线程原子性操作,也就是在修改时我们只需要保证它的那个瞬间是安全的即可,经过相应的包装后可以再处理对象的并发修改,本文总结一下Atomic系列的类的使用方法,其中包含:

基本类型    AtomicInteger    AtomicLong    AtomicBoolean

数组类型    AtomicIntegerArray    AtomicLongArray    AtomicReferenceArray

属性原子修改器    AtomicIntegerFieldUpdater    AtomicLongFieldUpdater    AtomicReferenceFieldUpdater

看java.util.concurrent包及子包的API帮助文档

看concurrent包的帮助文档页面,对并发库中涉及的内容有一个总体上的介绍:在并发编程中很常用的实用工具类。

如何看包的API帮助文档:可以先找到该包下的某个类的帮助页面,然后在该页面的顶部单击package超链接。

java.util.concurrent.atomic包

查看atomic包文档页下面的介绍:类的小工具包,支持在单个变量上解除锁的线程安全编程。

可以对基本数据,对数组中的基本数据,对类中的基本数据等进行操作 

通过如下两个方法快速理解atomic包的意义:

AtomicInteger类的boolean compareAndSet(expectedValue, updateValue); 

AtomicIntegerArray类的int addAndGet(int i, int delta);

顺带解释volatile类型的作用,需要查看java语言规范。Volatile的意思是说:在jvm中,一个线程更新了共享变量i,另外一个线程立即去读取共享区中的i时,读到的可能不是刚才另外那个线程更新过的结果,这就类似数据库中的事务隔离级别中的read uncommited,volatile就是解决这个问题的。

 AtomicInteger类

           java.util.concurrent.atomic包下的AtomicInteger类可以解决多线程访问同一个整数的问题

首先通过构造函数AtomicInteger(int initialValue)创建一个给定值的原子整数

然后比如调用对象上的addAndGet(int delta)方法返回对象中的数和delta的和(传入负数就是相减),该方法的调用过程中别的线程无法参与进来,因此自动实现了线程同步。还有其他的方法比如decrementAndGet()和incrementAndGet()

一般用于定义被多线程访问的成员变量而不是局部变量,因为局部变量会在每个线程中都有一份

类似的有AtomicBoolean、AtomicLong类操作不同的数据。

 常见的方法列表

 AtomicInteger.get()             直接返回值

AtomicInteger.getAndAdd(int)    增加指定的数据,返回变化前的数据

 AtomicInteger.getAndDecrement() 减少1,返回减少前的数据

 AtomicInteger.getAndIncrement() 增加1,返回增加前的数据

AtomicInteger.getAndSet(int)    设置指定的数据,返回设置前的数据

AtomicInteger.addAndGet(int)    增加指定的数据后返回增加后的数据

AtomicInteger.decrementAndGet() 减少1,返回减少后的值

AtomicInteger.incrementAndGet() 增加1,返回增加后的值

AtomicInteger.lazySet(int)      仅仅当get时才会set

 AtomicInteger.compareAndSet(int, int) 尝试新增后对比,若增加成功则返回true否则返回false

import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {

    public final static AtomicInteger TEST_INTEGER = new AtomicInteger(1);

    public static void main(String []args) {

         for(int i = 0 ; i < 10 ; i++) { //开启10个线程

             new Thread() {
                 public void run() {
                     try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    int now = TEST_INTEGER.incrementAndGet(); //自增
                    System.out.println(Thread.currentThread().getName() + " get value : " + now);
                 }
             }.start();
         }
    }
}

  从运行结果可以看出,10个线程之间是线程安全的,并没有冲突。也就是说,我们使用原子性操作类去操作基本类型int就可以解决线程安全问题,一个线程在操作的时候,会对其它线程进行排斥,不用我们手动去使用synchronized实现互斥操作了。AtomicLong和AtomicBoolean类似,就不举例子了。

AtomicIntegerArray类

 java.util.concurrent.atomic包下的AtomicIntegerArray类用于解决多线程操作数组中的一个整数的问题,Atomic的数组要求不允许修改长度等,不像集合类那么丰富的操作,不过它可以让数组上每个元素的操作绝对安全的,也就是它细化的力度还是到数组上的元素,做了二次包装,虽然是数组类型的,但是最后还是操作数组中存的数,所以会了上面的基本类型的话,数组类型也很好理解。这里主要看一下AtomicIntegerArray的使用,其它的类似.
           常用方法:

AtomicIntegerArray.addAndGet(int, int) 执行加法,第一个参数为数组的下标,第二个参数为增加的数量,返回增加后的结果

AtomicIntegerArray.compareAndSet(int, int, int) 对比修改,参数1:数组下标,参数2:原始值,参数3,修改目标值,修改成功返回true否则false

AtomicIntegerArray.decrementAndGet(int) 参数为数组下标,将数组对应数字减少1,返回减少后的数据

AtomicIntegerArray.incrementAndGet(int) 参数为数组下标,将数组对应数字增加1,返回增加后的数据

AtomicIntegerArray.getAndAdd(int, int) 和addAndGet类似,区别是返回值是变化前的数据

AtomicIntegerArray.getAndDecrement(int) 和decrementAndGet类似,区别是返回变化前的数据

AtomicIntegerArray.getAndIncrement(int) 和incrementAndGet类似,区别是返回变化前的数据

AtomicIntegerArray.getAndSet(int, int) 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
 

public class AtomicIntegerArrayTest {

    /**
     * 常见的方法列表
     * @see AtomicIntegerArray#addAndGet(int, int) 执行加法,第一个参数为数组的下标,第二个参数为增加的数量,返回增加后的结果
     * @see AtomicIntegerArray#compareAndSet(int, int, int) 对比修改,参数1:数组下标,参数2:原始值,参数3,修改目标值,修改成功返回true否则false
     * @see AtomicIntegerArray#decrementAndGet(int) 参数为数组下标,将数组对应数字减少1,返回减少后的数据
     * @see AtomicIntegerArray#incrementAndGet(int) 参数为数组下标,将数组对应数字增加1,返回增加后的数据
     * 
     * @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet类似,区别是返回值是变化前的数据
     * @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet类似,区别是返回变化前的数据
     * @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet类似,区别是返回变化前的数据
     * @see AtomicIntegerArray#getAndSet(int, int) 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
     */
    private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{1,2,3,4,5,6,7,8,9,10});

    public static void main(String []args) throws InterruptedException {
        Thread []threads = new Thread[10];
        for(int i = 0 ; i < 10 ; i++) {
            final int index = i;
            final int threadNum = i;
            threads[i] = new Thread() {
                public void run() {
                    int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
                    System.out.println("线程编号为:" + threadNum + " , 对应的原始值为:" + (index + 1) + ",增加后的结果为:" + result);
                }
            };
            threads[i].start();
        }
        for(Thread thread : threads) {
            thread.join();
        }
        System.out.println("=========================>\n执行已经完成,结果列表:");
        for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
            System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
        }
    }
}

  运行结果是给每个数组元素加上相同的值,它们之间互不影响。

AtomicIntegerFieldUpdater类

解决多线程操作类的对象中存储的整数的问题

常用方法(说明上和AtomicInteger几乎一致,唯一的区别是第一个参数需要传入对象的引用) 

AtomicIntegerFieldUpdater.addAndGet(Object, int) 

AtomicIntegerFieldUpdater.compareAndSet(Object, int, int)

AtomicIntegerFieldUpdater.decrementAndGet(Object) 

AtomicIntegerFieldUpdater.incrementAndGet(Object)  

AtomicIntegerFieldUpdater.getAndAdd(Object, int)   

AtomicIntegerFieldUpdater.getAndDecrement(Object)   

AtomicIntegerFieldUpdater.getAndIncrement(Object) 

AtomicIntegerFieldUpdater.getAndSet(Object, int) 

public class AtomicIntegerFieldUpdaterTest {  

    static class A {  
        volatile int intValue = 100;  
    }  

    public final static AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");  

    public static void main(String []args) {  
        final A a = new A();  
        for(int i = 0 ; i < 10 ; i++) {  

            new Thread() {  
                public void run() {  
                    if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) {  
                        System.out.println(Thread.currentThread().getName() + " 对对应的值做了修改!");  
                    }  
                }  
            }.start();  
        }  
    }  
}  

        可以看到,这里需要将类和类属性传进去才行,传进去后其实跟前面操作Integer没什么不同了,本质都一样的,运行一下,结果只有一个线程能对其进行修改。 
  线程的原子性操作类的使用就简单总结到这,其他的操作类原理都相似,可以参考JDK的文档,可以很容易写出相应的测试代码。 

猜你喜欢

转载自blog.csdn.net/yucaixiang/article/details/89211550