什麼是比賽條件?

在編寫多線程應用程序時,遇到的最多見問題之一是競爭條件。 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。 您還能夠在此處得到有關上述說明的更多詳細信息

相關文章
相關標籤/搜索