• Java多线程_Atomic


    1.什么是Atomic?
    Atomic,中文意思是“原子的”,在java多线程中,有这样的一个包:

    java.util.concurrent.atomic——线程安全的原子操作包

    这是JDK1.5的版本中为我们提供的原子操作包。所谓“原子”操作,是指一组不可分割的操作,操作者对目标对象进行操作时,要么完成所有操作后其他操作者才能操作;要么这个操作者不能进行任何操作。

    2.Atomic有什么作用?
    原子操作包为我们提供了四类原子操作:

    • 原子更新基本类型
    • 原子更新数组
    • 原子更新引用
    • 原子更新字段。

    通过这四类,可以有效地解决实际开发中遇到脏数据的问题。

    3.原子更新基本类型
    原子更新基本类型中有三种操作:

    • AtomicBoolean:布尔数据的原子操作
    • AtomicInteger:整型数字的原子操作
    • AtomicLong:长整型数字的原子操作

    下面通过一段代码实现一个AtomicInteger的累加器:

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicIntegerDemo {
        public static void main(String[] args) {
            Add add = new Add();
            Thread t1 = new Thread(add);
            Thread t2 = new Thread(add);
            t1.start();
            t2.start();
        }
    }
    
    class Add implements Runnable {
        AtomicInteger count = new AtomicInteger(0);
    
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                System.out.println(Thread.currentThread().getName() + " " + count.incrementAndGet());
            }
        }
    }

    从上面的代码我们可以看出,这里并没有使用synchronized就实现了线程安全,这是为什么呢?
    我们不妨看一下源码

    public final int incrementAndGet() { 
        for (;;) { 
            int current = get(); 
            int next = current + 1; 
            if (compareAndSet(current, next)) 
                return next; 
        } 
    }
    public final boolean compareAndSet(int expect, int update) {    
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
    } 

    那源码中为什么是一个死循环呢?
    这里要引出两个概念——悲观锁和乐观锁
    悲观锁是一种独占锁,它假设的前提是“冲突一定会发生”,所以处理某段可能出现数据冲突的代码时,这个代码段就要被某个线程独占。而独占意味着“其它即将执行这段代码的其他线程”都将进入“阻塞”/“挂起”状态。synchronized关键字就是java对于悲观锁的实现。
    乐观锁假定“冲突不一定会出现”,如果出现冲突则进行重试,直到冲突消失。 由于乐观锁的假定条件,所以乐观锁不会独占资源性能自然在多数情况下就会好于悲观锁,但也会有少数情况坏于悲观锁,就是一旦多线程对某个资源的抢占频度达到了某种规模,就会导致乐观锁内部出现多次更新失败的情况,最终造成乐观锁内部进入一种“活锁”状态。AtomicInteger是一个标准的乐观锁实现

    所以说在incrementAndGet()中,会有一个“死循环”,这是incrementAndGet()方法会有“比较-重试”的过程。

    4.原子更新数组

    • AtomicIntegerArray:原子操作整型数组
    • AtomicLongArray:原子操作长整型数组
    • AtomicReferenceArray:原子操作对象引用数组

    下面通过一段代码实现一个AtomicIntegerArray的累加器:

    import java.util.concurrent.atomic.AtomicIntegerArray;
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    public class AtomicIntegerArrayDemo {
        public static void main(String[] args) {
            AddTest add = new AddTest();
            Thread[] t = new Thread[10];
            for (int i = 0; i < 10; i++) {
                t[i] = new Thread(add);
            }
            for (int i = 0; i < 10; i++) {
                t[i].start();
            }
            for (int i = 0; i < 10; i++) {
                try {
                    t[i].join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(add.arr);
        }
    
        static class AddTest implements Runnable {
            AtomicIntegerArray arr = new AtomicIntegerArray(10);
    
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    arr.getAndIncrement(i % arr.length());
                }
            }
        }
    }

    我们不妨看一下输出:

    由此可知,AtomicIntegerArray是线程安全的。

    5.原子更新引用及字段
    原子更新对象和字段的操作有:

    • AtomicIntegerFieldUpdater:整型数据字段更新器
    • AtomicLongFieldUpdater:长型数据字段更新器
    • AtomicReferenceFieldUpdater:对象数据字段更新器
    • AtomicReference:对象原子操作

    我们用AtomicReferenceFieldUpdater更新字段操作:

    import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
    
    public class AtomicReferenceUpdaterDemo {
        public static void main(String[] args) {
            Student student = new Student();
            student.setName("StudentA");
            Score score = new Score();
            score.setScore(80);
            student.setScore(score);
            Score newScore = new Score();
            newScore.setScore(90);
            student.setScore(newScore);
            System.out.println(student.getName() + ":旧成绩" + score.getScore() + "  新成绩:" + newScore.getScore());
        }
    }
    
    class Student {
        private String name;
        private volatile Score score;
    
        public Student(String name, Score score) {
            super();
            this.name = name;
            this.score = score;
        }
    
        public Student() {
            super();
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Score getScore() {
            return score;
        }
    
        public void setScore(Score score) {
            score_update.set(this, score);
        }
    
        public AtomicReferenceFieldUpdater<Student, Score> score_update = AtomicReferenceFieldUpdater
                .newUpdater(Student.class, Score.class, "score");
    }
    
    class Score {
        private int score;
    
        public Score() {
            super();
        }
    
        public Score(int score) {
            super();
            this.score = score;
        }
    
        public int getScore() {
            return score;
        }
    
        public void setScore(int score) {
            this.score = score;
        }
    }

    输出:

    下面我们来看一段关于AtomicReference的代码(实现自动充值,当少于20元时,充值20元,再进行消费,每次消费10元):

    import java.util.concurrent.atomic.AtomicReference;
    
    public class AtomicReferenceDemo {
        static AtomicReference<Integer> money = new AtomicReference<Integer>(19);
    
        public static void main(String[] args) {
            Add add = new Add();
            Thread thread1 = new Thread(add);
            thread1.start();
            Reduce reduce = new Reduce();
            Thread thread2 = new Thread(reduce);
            thread2.start();
        }
    
        static class Add implements Runnable {
    
            @Override
            public void run() {
                while (true) {
                    while (true) {
                        Integer m = money.get();
                        if (m < 20) {
                            if (money.compareAndSet(m, m + 20)) {
                                System.out.println("充值成功,余额:" + money.get() + "元");
                                break;
                            }
                        } else {
                            break;
                        }
                    }
                }
            }
        }
    
        static class Reduce implements Runnable {
    
            @Override
            public void run() {
                while (true) {
                    while (true) {
                        Integer m = money.get();
                        if (m > 10) {
                            if (money.compareAndSet(m, m - 10)) {
                                System.out.println("成功消费10元,余额:" + money.get() + "元");
                                break;
                            }
                        } else {
                            break;
                        }
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    它的结果是这样的:

    其实这种方法还是可能会出现cas无法判断数据状态的情况,虽然概率不大,但是一般也不采用这种方法。这儿我们先不着急,在我CAS算法和ABA问题的博客中,我们会引出这种现象以及解决方案。

    最后,补充两点:

    (1)CAS如果长时间不成功,会极大的增加CPU的开销。所以CAS不适合用在复杂的场景。

    (2)AtomicIntegerFieldUpdater只能更新int型的数据,并不能Integer数据,要更新Integer数据要使用AtomicReferenceFieldUpdater。 

  • 相关阅读:
    [LeetCode]78. Remove Nth Node From end of List删除链表中倒数第N个节点
    [LeetCode]77. Reverse Linked List反转链表
    [LeetCode]76. Permutation Sequence全排列序列
    [LeetCode]75. Pow(x,n)幂运算
    粘连字符分割初探~~
    验证码识别学习~~
    用VS2010编C#程序扫盲 2
    用VS2010编C#程序扫盲
    验证码降噪方法汇总~~~~~
    新生活......
  • 原文地址:https://www.cnblogs.com/ericz2j/p/10288310.html
Copyright © 2020-2023  润新知