在并发编程中咱们常说的“竞态”是什么?

1 何谓“竞态”

以前在学习一篇文章的时候,就看到“竞态”,可是不知道什么意思,文章中也没有对“竞态”作更多的解释,后来通过一番的探索,终于弄的差很少明白了,今天写点总结。 首先,咱们要明白“竞态”是什么。先说个人结论吧,“竞态”就是在多线程的编程中,你在同一段代码里输入了相同的条件,可是会输出不肯定的结果的状况。我不知道这个解释是否是够清楚,咱们接着往下看,下面咱们用一段代码来解释一下啊。 出现竞态条件的代码:java

public class MineRaceConditionDemo {
    private int sharedValue = 0;
    private final static int MAX = 1000;
    private int raceCondition() {
        if (sharedValue < MAX) {
            sharedValue++;
        } else {
            sharedValue = 0;
        }
        return sharedValue;
    }

    public static void main(String[] args) {
        MineRaceConditionDemo m = new MineRaceConditionDemo();
        ExecutorService es = new ThreadPoolExecutor(10,
                10,
                5,
                TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(1000),
                new ThreadPoolExecutor.CallerRunsPolicy());
        ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();

        for (int i = 0; i < 1000; i++) {
            es.execute(() -> {
                try {
                // 这是精髓所在啊,若是没有这个,那么要跑好几回才会出现竞态条件。
                // 这个用来模拟程序中别的代码的处理时间。
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int num = m.raceCondition();
                if (map.get(num) != null) {
                    System.out.println("the repeat num: " + num);
                    System.out.println("happen.");
                } else {
                    map.put(num, 0);
                }
            });
        }
        es.shutdown();
    }
}
复制代码

以上的代码是我本身的设计的一段会出现竞态条件的代码,比较简陋,可是能够说明问题了,你只要运行上面这段代码,每次的输出的结果级大几率都是不一样的,可是也有之外,好比你的电脑的性能很强,这段代码也会出现执行正确的状况,也就是啥也不输出。 好比有的时候输出这个:编程

the repeat num: 78
happen.
the repeat num: 229
happen.
the repeat num: 267
happen.
the repeat num: 267
happen.
the repeat num: 498
happen.

复制代码

有点时候输出这个:多线程

the repeat num: 25
happen.
the repeat num: 157
happen.
复制代码

固然,以上的是个人输出的,大家的输出确定也是不一样的。 对于上面这些,同一段代码,对于一样的输出,可是程序的输出有的时候是正确,有的时候是错误的,这种状况,咱们称之为“竞态”。最要命的就是,代码每次输出不是每次都错误,而是你不知道他何时会正确,何时会错误。 固然,若是以上的代码执行的状况就是,啥都不输出,全部的值都是惟一的。app

2 “竞态”为何会发生?

“竞态”的发生主要是由于多个线程都对一个共享变量(好比上面的 sharedValue 就属于共享变量)有读取-修改的操做。在某个线程读取共享变量以后,进行相关操做的时候,别的线程把这个变量给改了,从而致使结果出现了错误。工具

什么样的代码模式会发生“竞态”

这部分知识主要是来自《Java多线程编程实战指南 核心篇》。 这里书中提到,会发生竞态条件就是两个模式:read-modify-write(读-改-写)和 check-than-act(检测然后行动)。 固然,这里面的都有一个相同的操做过程:某个有读取这个“共享变量”的操做,而后别的线程有个修改这个变量的操做。这里有个重点,在多个线程中,起码有一个线程有更新操做;若是全部的线程都是读操做,那么就不存在什么竞态条件。 整体来讲,就是要thread1#load - thread2#update。 这种的模式,起码是是要有两个线程的,并且其中某个线程确定是要有更新“共享变量”操做的,另外一个线程不论是读取变量仍是更新变量都会出现错误(要么读取脏数据、要么丢失更新结果)。性能

3 如何消除“竞态”?

单以上面的操做来讲,通常来讲有两种解法方式,学习

3.1 加锁

加上synchronized关键字,保证每次只能有一个线程获取共享变量的使用权。spa

private synchronized int raceCondition() {
    if (sharedValue < MAX) {
        sharedValue++;
    } else {
        sharedValue = 0;
    }
    return sharedValue;
}
复制代码

3.2 利用原子操做

利用java的工具包里的 AtomicInteger,代替int,利用原子操做消除“竞态”。线程

private AtomicInteger sharedValue = new AtomicInteger(0);
private final static int MAX = 1000;
private int raceCondition() {
    if (sharedValue.get() < MAX) {
        return sharedValue.getAndIncrement();
    } else {
        sharedValue.set(0);
        return sharedValue.get();
    }
}
复制代码

以上两种方法的要义就是保证每一个线程在操做“共享变量”的都是原子操做。设计

相关文章
相关标签/搜索