什么是比赛条件?

在编写多线程应用程序时,遇到的最多见问题之一是竞争条件。 java

我对社区的问题是: 编程

什么是比赛条件? 您如何检测到它们? 您如何处理它们? 最后,如何防止它们发生? 数据结构


#1楼

当设备或系统试图同时执行两个或多个操做时,竞争状态是一种不但愿出现的状况,可是因为设备或系统的性质,必须按照正确的顺序进行操做才能被执行。正确完成。 多线程

在计算机内存或存储中,若是几乎在同一时刻接收到读取和写入大量数据的命令,而且机器尝试覆盖部分或所有旧数据,而仍旧保留旧数据,则可能会发生竞争状态读。 结果多是如下一种或多种:计算机崩溃,“非法操做”,程序的通知和关闭,读取旧数据时出错或写入新数据时出错。 并发


#2楼

微软实际上已经发布了有关种族条件和僵局问题的很是详细的文章 。 其中最归纳的摘要是标题段落: oracle

当两个线程同时访问一个共享变量时,就会发生竞争状态。 第一个线程读取变量,第二个线程从变量读取相同的值。 而后,第一个线程和第二个线程对值执行操做,而后争先看哪一个线程能够最后将值写入共享变量。 保留最后写入其值的线程的值,由于该线程正在覆盖前一个线程写入的值。 函数


#3楼

请尝试如下基本示例,以更好地了解比赛条件: 工具

public class ThreadRaceCondition {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Account myAccount = new Account(22222222);

        // Expected deposit: 250
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.DEPOSIT, 5.00);
            t.start();
        }

        // Expected withdrawal: 50
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.WITHDRAW, 1.00);
            t.start();

        }

        // Temporary sleep to ensure all threads are completed. Don't use in
        // realworld :-)
        Thread.sleep(1000);
        // Expected account balance is 200
        System.out.println("Final Account Balance: "
                + myAccount.getAccountBalance());

    }

}

class Transaction extends Thread {

    public static enum TransactionType {
        DEPOSIT(1), WITHDRAW(2);

        private int value;

        private TransactionType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    };

    private TransactionType transactionType;
    private Account account;
    private double amount;

    /*
     * If transactionType == 1, deposit else if transactionType == 2 withdraw
     */
    public Transaction(Account account, TransactionType transactionType,
            double amount) {
        this.transactionType = transactionType;
        this.account = account;
        this.amount = amount;
    }

    public void run() {
        switch (this.transactionType) {
        case DEPOSIT:
            deposit();
            printBalance();
            break;
        case WITHDRAW:
            withdraw();
            printBalance();
            break;
        default:
            System.out.println("NOT A VALID TRANSACTION");
        }
        ;
    }

    public void deposit() {
        this.account.deposit(this.amount);
    }

    public void withdraw() {
        this.account.withdraw(amount);
    }

    public void printBalance() {
        System.out.println(Thread.currentThread().getName()
                + " : TransactionType: " + this.transactionType + ", Amount: "
                + this.amount);
        System.out.println("Account Balance: "
                + this.account.getAccountBalance());
    }
}

class Account {
    private int accountNumber;
    private double accountBalance;

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getAccountBalance() {
        return accountBalance;
    }

    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean deposit(double amount) {
        if (amount < 0) {
            return false;
        } else {
            accountBalance = accountBalance + amount;
            return true;
        }
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean withdraw(double amount) {
        if (amount > accountBalance) {
            return false;
        } else {
            accountBalance = accountBalance - amount;
            return true;
        }
    }
}

#4楼

竞争条件和数据竞争之间存在重要的技术差别。 大多数答案彷佛都假设这些术语是等效的,但事实并不是如此。 单元测试

当2条指令访问相同的存储器位置时发生数据争用,这些访问中的至少一个是写操做,而且在这些访问之间进行排序以前没有发生任何状况 。 如今,关于在顺序以前发生的事件的争论不少,可是一般在同一锁定变量上的ulock-lock对和在同一条件变量上的wait-signal对会致使发生先于顺序。 测试

竞争条件是语义错误。 这是在事件的时间安排或顺序中出现的缺陷,致使错误的程序行为

许多竞争条件多是(其实是)数据竞争引发的,但这不是必需的。 实际上,数据争用和争用条件既不是彼此的必要条件也不是充分条件。 这篇博客文章还经过一个简单的银行交易示例很好地解释了差别。 这是另外一个简单的示例 ,解释了它们之间的区别。

如今咱们已经肯定了术语,让咱们尝试回答原始问题。

因为种族条件是语义错误,所以没有检测它们的通用方法。 这是由于在一般状况下,没法使用自动的oracle来区分正确的程序行为与错误的程序行为。 种族检测是一个没法肯定的问题。

另外一方面,数据竞争具备不必定与正确性相关的精肯定义,所以人们能够检测到它们。 数据争用检测器有不少类型(静态/动态数据争用检测,基于锁集的数据争用检测,基于事前发生的数据争用检测,混合数据争用检测)。 最早进的动态数据竞争检测器是ThreadSanitizer ,在实践中效果很好。

一般,处理数据争用须要必定的编程纪律来诱发(在开发期间或使用上述工具检测到它们之间)访问共享数据之间的边缘以前。 这能够经过锁,条件变量,信号量等来完成。可是,也能够采用不一样的编程范例,例如消息传递(而不是共享内存)来避免构造过程当中的数据争用。


#5楼

什么是比赛条件?

您打算在下午5点去看电影。 您在下午4点询问门票的供应状况。 该表明说,他们有空。 放映前5分钟,您能够放松身心并到达售票窗口。 我敢确定,您能够猜想会发生什么:这是一间完整的房子。 这里的问题在于检查和操做之间的持续时间。 您在4咨询并在5采起行动。与此同时,其余人则抢了票。 那是比赛条件-特别是比赛条件的“先检查后行动”场景。

您如何检测到它们?

宗教代码审查,多线程单元测试。 没有捷径。 不多有Eclipse插件出现,可是尚未稳定的东西。

您如何处理和预防它们?

最好的办法是建立无反作用的无状态函数,并尽量多地使用不可变对象。 但这并不老是可能的。 所以,使用java.util.concurrent.atomic,并发数据结构,正确的同步以及基于actor的并发性将有所帮助。

最佳的并发资源是JCIP。 您还能够在此处得到有关上述说明的更多详细信息

相关文章
相关标签/搜索