java并发——原子性操作类的使用

包含类型

​ 在java5以后,有了线程原子性操作,也就是在修改的时候,我们只需要保证他的那个瞬间是安全的即可,经过响应的包装后可以再对对象的并发修改,主要涉及到Atomic序列类的使用方法。

类型 Interger Long
基本类型 AtomicInteger AtomicLong AtomicBoolean
数组类型 AtomicIntegerArray AtomicLongArray AtomicReferenceArray
属性原子修改器 AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater

​ 主要就是这三种,下面将分别介绍着三种类型

基本类型的使用

AtomicInteger

​ 主要是针对整数的修改

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {

    /**
     * 常见的方法列表
     * @see AtomicInteger#get()             直接返回值
     * @see AtomicInteger#getAndAdd(int)    增加指定的数据,返回变化前的数据
     * @see AtomicInteger#getAndDecrement() 减少1,返回减少前的数据
     * @see AtomicInteger#getAndIncrement() 增加1,返回增加前的数据
     * @see AtomicInteger#getAndSet(int)    设置指定的数据,返回设置前的数据
     *
     * @see AtomicInteger#addAndGet(int)    增加指定的数据后返回增加后的数据
     * @see AtomicInteger#decrementAndGet() 减少1,返回减少后的值
     * @see AtomicInteger#incrementAndGet() 增加1,返回增加后的值
     * @see AtomicInteger#lazySet(int)      仅仅当get时才会set
     *
     * @see AtomicInteger#compareAndSet(int, int) 尝试新增后对比,若增加成功则返回true否则返回false
     */
    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();
        }
    }
}
  • 结果:

​ 可以看到,十个线程之间是线程安全的,并没有冲突,也就是使用原子性操作类去操作基本类型int就可以解决线程安全问题,一个线程在操作的时候,会对其他线程进行排斥,不用我们去手动使用synchronized实现互斥操作了。其余的也类似

  • 对比
public class AtomicIntegerDemo {

   public static int a=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=a++;
                    System.out.println(Thread.currentThread().getName() + " get value : " + now);
                }
            }.start();
        }
    }
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ro93FOx-1577670530371)(C:\Users\sunmingzhi\AppData\Roaming\Typora\typora-user-images\1577520903887.png)]

就会出现线程不安全的情况。

数组类型的使用

​ Atomic的数组用法:首先Atomic的数组要求不允许修改长度等,不想集合类那么丰富的操作,不过它可实现让数组上每个元素的操作绝对安全的,也就是它细化的力度还是到数组上的元素,做了二次包装,虽然是数组类型的,但是最后还是操作数组中存的数。下面主要介绍AtomicIntegerArray的使用

import java.util.concurrent.atomic.AtomicIntegerArray;

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));
        }
    }
}

  • 结果

扫描二维码关注公众号,回复: 8637831 查看本文章

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

作为类属性的使用

​ 当某个数据类型是某个类中的一个属性的时候,然后我们要操作该数据,就需要使用属性原子修改器了,这里还是以Integer为例。即:AtomicIntegerFieldUpdater

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterTest {

    static class A {
        volatile int intValue = 100;
    }

    /**
     * 可以直接访问对应的变量,进行修改和处理
     * 条件:要在可访问的区域内,如果是private或挎包访问default类型以及非父亲类的protected均无法访问到
     * 其次访问对象不能是static类型的变量(因为在计算属性的偏移量的时候无法计算),也不能是final类型的变量(因为根本无法修改),必须是普通的成员变量
     *
     * 方法(说明上和AtomicInteger几乎一致,唯一的区别是第一个参数需要传入对象的引用)
     * @see AtomicIntegerFieldUpdater#addAndGet(Object, int)
     * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int)
     * @see AtomicIntegerFieldUpdater#decrementAndGet(Object)
     * @see AtomicIntegerFieldUpdater#incrementAndGet(Object)
     *
     * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int)
     * @see AtomicIntegerFieldUpdater#getAndDecrement(Object)
     * @see AtomicIntegerFieldUpdater#getAndIncrement(Object)
     * @see AtomicIntegerFieldUpdater#getAndSet(Object, int)
     */
    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没什么不同了,本质都一样。可以看到结果只有一个线程能对其进行修改。

发布了47 篇原创文章 · 获赞 35 · 访问量 3240

猜你喜欢

转载自blog.csdn.net/issunmingzhi/article/details/103761443
今日推荐