美文网首页程序员Android高级技术Android面试
Java多线程3-原子性操作类的使用

Java多线程3-原子性操作类的使用

作者: 香沙小熊 | 来源:发表于2018-11-26 21:55 被阅读44次

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

原子更新类型 名称 描述
基本类型 AtomicBoolean 原子更新布尔类型
基本类型 AtomicInteger 原子更新整型
基本类型 AtomicLong 原子更新长整型
数组类型 AtomicIntegerArray 原子更新整型数组里的元素
数组类型 AtomicLongArray 原子更新长整型数组里的元素
数组类型 AtomicReferenceArray 原子更新引用类型数组的元素
数组类型 AtomicBooleanArray 原子更新布尔类型数组的元素
引用类型 AtomicReference 原子更新引用类型
引用类型 AtomicReferenceFieldUpdater 原子更新引用类型里的字段
引用类型 AtomicMarkableReference 原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和应用类型
字段类型 AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
字段类型 AtomicLongFieldUpdater 原子更新长整型字段的更新器
字段类型 AtomicStampedReference 原子更新带有版本号的引用类型。该类将整型数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

1. 基本类型的使用

public class AtomicTest {
    /**
     * 常见的方法列表
     *
     * @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 static void main(String[] args) {

        final AtomicTicket ticket = new AtomicTicket();
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (ticket.getCount() > 0) {
                        System.out.println(Thread.currentThread().getName() + " count: " + ticket.decrement());
                    }
                }
            }).start();
        }
    }
}


class AtomicTicket {

    public AtomicInteger count = new AtomicInteger(100);

    public int decrement() {

        return count.getAndDecrement();
    }

    public int getCount() {
        return count.get();

    }
}
Thread-0 count: 100
Thread-2 count: 98
Thread-1 count: 99
Thread-2 count: 96
Thread-0 count: 97
Thread-2 count: 94
Thread-2 count: 92
Thread-1 count: 95
中间省略...
Thread-1 count: 12
Thread-2 count: 7
Thread-0 count: 9
Thread-2 count: 5
Thread-1 count: 6
Thread-2 count: 3
Thread-0 count: 4
Thread-2 count: 1
Thread-1 count: 2

2. 数组类型的使用

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;
            threads[i] = new Thread() {
                public void run() {
                    int original =  ATOMIC_INTEGER_ARRAY.get(index);
                    int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
                    System.out.println("currentThread:" + Thread.currentThread().getName() + " , 原始值为:" + original + ",增加后的结果为:" + 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));
        }
    }
}

currentThread:Thread-0 , 原始值为:1,增加后的结果为:2
currentThread:Thread-3 , 原始值为:4,增加后的结果为:8
currentThread:Thread-2 , 原始值为:3,增加后的结果为:6
currentThread:Thread-1 , 原始值为:2,增加后的结果为:4
currentThread:Thread-5 , 原始值为:6,增加后的结果为:12
currentThread:Thread-4 , 原始值为:5,增加后的结果为:10
currentThread:Thread-6 , 原始值为:7,增加后的结果为:14
currentThread:Thread-7 , 原始值为:8,增加后的结果为:16
currentThread:Thread-8 , 原始值为:9,增加后的结果为:18
currentThread:Thread-9 , 原始值为:10,增加后的结果为:20
=========================>
执行已经完成,结果列表:
2
4
6
8
10
12
14
16
18
20

3. 引用类型的使用

public class AtomicReferenceTest {

    public static void main(String[] args) {
        People people1 =new People("Bom", 0);
        People people2 =new People("Tom",10);

        //先初始化一个值,如果不初始化则默认值为null
        AtomicReference<People> reference = new AtomicReference<>(people1);
        People people3 = reference.get();
        if (people3.equals(people1)) {
            System.out.println("people3:" + people3);
        } else {
            System.out.println("else:" + people3);
        }

        /**
         * 当前值:拿当前值和reference.get()获取到的值去比较,如果相等则true并更新值为期望值
         * 期望值:如果返回true则更新为期望值,如果返回false则不更新值
         */
        boolean b = reference.compareAndSet(null, people2);
        System.out.println("myClass.main-"+b+"--"+reference.get());

        boolean b1 = reference.compareAndSet(people1, people2);
        System.out.println("myClass.main-"+b1+"--"+reference.get());


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());

                People people = reference.get();
                people.setName("Tom"+Thread.currentThread().getName());
                people.setAge(people.getAge()+1);
                reference.getAndSet(people);
                System.out.println(Thread.currentThread().getName()+reference.get().toString());
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());

                People people = reference.get();
                people.setName("Tom"+Thread.currentThread().getName());
                people.setAge(people.getAge()+4);
                reference.getAndSet(people);
                System.out.println(Thread.currentThread().getName()+reference.get().toString());
            }
        }).start();

    }

}

 class People {
    private String name;
    private int age;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4.字段类型的使用

public class AtomicIntegerFieldUpdaterTest {

    /**
     * 可以直接访问对应的变量,进行修改和处理
     * 条件:要在可访问的区域内,如果是private或挎包访问default类型以及非父亲类的protected均无法访问到
     * 其次访问对象不能是static类型的变量(因为在计算属性的偏移量的时候无法计算),也不能是final类型的变量(因为根本无法修改),必须是普通的成员变量
     * <p>
     * 方法(说明上和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() {
                    System.out.println(
                            Thread.currentThread().getName() + " " + ATOMIC_INTEGER_UPDATER.get(a));
                    ATOMIC_INTEGER_UPDATER.addAndGet(a, 11);
                    System.out.println(
                            Thread.currentThread().getName() + " " + ATOMIC_INTEGER_UPDATER.get(a));
                    if (ATOMIC_INTEGER_UPDATER.compareAndSet(a, ATOMIC_INTEGER_UPDATER.get(a), 120)) {
                        System.out.println(Thread.currentThread().getName() + " 对应的值做了修改!");
                    }
                    System.out.println(
                            Thread.currentThread().getName() + " " + ATOMIC_INTEGER_UPDATER.get(a));
                }
            }.start();
        }
    }

    static class A {
        volatile int intValue = 100;
    }
}

Thread-0 100
Thread-2 100
Thread-1 100
Thread-2 122
Thread-3 111
Thread-5 120
Thread-0 111
Thread-5 142
Thread-3 131
Thread-2 对应的值做了修改!
Thread-2 120
Thread-8 120
Thread-4 133
Thread-1 133
Thread-9 142
Thread-4 142
Thread-4 对应的值做了修改!
Thread-8 131
Thread-3 对应的值做了修改!
Thread-3 120
Thread-7 120
Thread-7 131
Thread-5 对应的值做了修改!
Thread-5 120
Thread-6 120
Thread-0 对应的值做了修改!
Thread-6 131
Thread-7 对应的值做了修改!
Thread-8 对应的值做了修改!
Thread-4 120
Thread-9 131
Thread-1 对应的值做了修改!
Thread-9 对应的值做了修改!
Thread-8 120
Thread-7 120
Thread-6 对应的值做了修改!
Thread-0 131
Thread-6 120
Thread-9 120
Thread-1 120

相关文章

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • Java多线程3-原子性操作类的使用

    在java5以后,我们接触到了线程原子性操作,也就是在修改时我们只需要保证它的那个瞬间是安全的即可,经过相应的包装...

  • 并发编程的灵魂:CAS机制详解

    Java中提供了很多原子操作类来保证共享变量操作的原子性。这些原子操作的底层原理都是使用了CAS机制。在使用一门技...

  • 并发编程的灵魂:CAS机制详解

    Java中提供了很多原子操作类来保证共享变量操作的原子性。这些原子操作的底层原理都是使用了CAS机制。在使用一门技...

  • Java 多线程三大核心【转载】

    Java 多线程三大核心 原子性 Java 的原子性就和数据库事务的原子性差不多,一个操作中要么全部执行成功或者失...

  • Java自学-多线程 原子访问

    多线程 原子访问 步骤 1 : 原子性操作概念 所谓的原子性操作即不可中断的操作,比如赋值操作 原子性操作本身是线...

  • java多线程 操作共享变量 保证原子性

    java多线程 操作共享变量 保证原子性 背景:使用多线程统计所有员工每月考勤数据,第一次迟到警告,之后按照规则扣...

  • AtomicInteger源码分析

    **AtomicInteger** 是Java提供的原子操作类,其内部通过 **UnSafe** 工具类,使用 =...

  • java并发编程(三)

    java多线程编程(三) 1.术语定义 2.处理器如何实现原子操作 (1)使用总线锁保证原子性 第一个机制是通过总...

  • 关于AtomicInteger

    AtomicInteger JAVA原子操作的Interger类, 主要为解决多线程线程安全问题,今天拿来测试一下...

网友评论

    本文标题:Java多线程3-原子性操作类的使用

    本文链接:https://www.haomeiwen.com/subject/hvyiqqtx.html