JAVA多线程基础:竞态条件

竞态条件定义

竞态条件会使得结果变得不可靠。当某个计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。

最常见的竞态条件为,“先检查后执行(Check-Then-Act )”即通过一个可能失效的观测结果决定下一步动作。

代码示例

/**
 * @author eventime
 * 竞态条件错误演示代码
 */
public class RaceCondition {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        // 10个线程执行同一个操作,count增1
        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(() -> count ++);
            thread.start();
        }
        Thread.sleep(5000);
        // 线程执行完毕后输出最终的count值
        System.out.println(count);
    }
}

运行五次代码得到如下结果:

9999,9999,10000,10000,10000

代码中我们创建了10000个线程对count进行 +1 操作。最终理想的情况下,count的值应该为10000;

但是前两次结果都出现了9999,这说明我们的代码并发出现了问题。

代码错误分析

有编程基础的读者应该知道代码** count ++** 其实是** count = count + 1,这里的操作并不是一个原子操作,而是由三个步骤组成。**

  1. 读取count
  2. 使count增1
  3. 写回count

这样也就造成了上文代码所出现的竞态条件问题。
在这里插入图片描述

从图中我们可以明显的看到错误所在的地方,即在线程1还未将count写回之前,线程2便读取了count。两线程都是在count = 0的基础上对count进行操作,这也就导致了最后的结果错误,两个线程进行了相同的工作。

线程2对count的操作依据的是一个可能过时的count。即通过一个可能失效的观测结果决定下一步动作。

解决办法

通过以上分析,我们发现,如果使得线程操作时一直保证count不失效, 那么就能避免这个问题;

/**
 * @author eventime
 * 竞态条件改进演示代码
 */
public class RaceCondition {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        // 10个线程执行同一个操作,count增1
        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(() -> {
                synchronized (RaceCondition.class) { // 锁定类对象
                    count++;
                }
            });
            thread.start();
        }
        Thread.sleep(5000);
        // 线程执行完毕后输出最终的count值
        System.out.println(count);
    }
}

我们通过对类上锁使得同一时间只有一个线程能够访问count,也就是说无法count在一个线程操作时候不用担心失效问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值