在编写多线程应用程序时,遇到的最常见的问题之一是竞争条件。
我对社区的问题是:
竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?
在编写多线程应用程序时,遇到的最常见的问题之一是竞争条件。
我对社区的问题是:
竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?
当前回答
一个有点规范的定义是“当两个线程同时访问内存中的同一个位置,并且至少有一次访问是写操作。”在这种情况下,“reader”线程可能获得旧值或新值,这取决于哪个线程“赢得了比赛”。这并不总是一个bug——事实上,一些非常复杂的低级算法会故意这样做——但通常应该避免。@Steve Gury的例子很好地说明了这可能是个问题。
其他回答
如果你使用“原子”类,你可以防止竞争条件。原因是线程没有分开操作get和set,示例如下:
AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);
因此,你将有7在链接“ai”。 虽然你做了两个操作,但这两个操作都确认了同一个线程,没有其他线程会干涉这,这意味着没有竞争条件!
下面是一个经典的银行账户余额示例,它将帮助新手轻松理解Java中的线程w.r.t.竞争条件:
public class BankAccount {
/**
* @param args
*/
int accountNumber;
double accountBalance;
public synchronized boolean Deposit(double amount){
double newAccountBalance=0;
if(amount<=0){
return false;
}
else {
newAccountBalance = accountBalance+amount;
accountBalance=newAccountBalance;
return true;
}
}
public synchronized boolean Withdraw(double amount){
double newAccountBalance=0;
if(amount>accountBalance){
return false;
}
else{
newAccountBalance = accountBalance-amount;
accountBalance=newAccountBalance;
return true;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BankAccount b = new BankAccount();
b.accountBalance=2000;
System.out.println(b.Withdraw(3000));
}
微软实际上已经发布了一篇关于竞态条件和死锁的非常详细的文章。最概括的摘要是标题段:
A race condition occurs when two threads access a shared variable at the same time. The first thread reads the variable, and the second thread reads the same value from the variable. Then the first thread and second thread perform their operations on the value, and they race to see which thread can write the value last to the shared variable. The value of the thread that writes its value last is preserved, because the thread is writing over the value that the previous thread wrote.
Race conditions occur in multi-threaded applications or multi-process systems. A race condition, at its most basic, is anything that makes the assumption that two things not in the same thread or process will happen in a particular order, without taking steps to ensure that they do. This happens commonly when two threads are passing messages by setting and checking member variables of a class both can access. There's almost always a race condition when one thread calls sleep to give another thread time to finish a task (unless that sleep is in a loop, with some checking mechanism).
防止竞争条件的工具依赖于语言和操作系统,但一些常见的工具是互斥锁、临界区和信号。互斥锁在你想确保你是唯一一个在做某事的时候很有用。当你想确保别人已经完成某件事时,信号是很好的。最小化共享资源还有助于防止意外行为
Detecting race conditions can be difficult, but there are a couple signs. Code which relies heavily on sleeps is prone to race conditions, so first check for calls to sleep in the affected code. Adding particularly long sleeps can also be used for debugging to try and force a particular order of events. This can be useful for reproducing the behavior, seeing if you can make it disappear by changing the timing of things, and for testing solutions put in place. The sleeps should be removed after debugging.
但是,如果某个问题只在某些机器上断断续续地发生,则是存在竞争条件的标志性标志。常见的错误是崩溃和死锁。使用日志记录,您应该能够找到受影响的区域并从那里返回。
什么是竞态条件?
过程严重依赖于其他事件的顺序或时间的情况。
例如, 处理器A和处理器B的执行都需要相同的资源。
你如何发现它们?
有一些工具可以自动检测竞态状态:
基于锁集的竞赛检查器 发生在种族检测之前 杂交种族检测
你是如何处理的?
竞争条件可以由互斥量或信号量处理。它们就像锁一样,允许进程根据特定的需求获取资源,以防止竞争。
你如何防止它们的发生?
防止竞争状态的方法有很多种,比如避免临界区。
没有两个进程同时在它们的关键区域内。(互斥) 没有对速度或cpu数量做任何假设。 没有进程运行在阻塞其他进程的关键区域之外。 没有进程需要永远等待才能进入临界区。(A等待B资源,B等待C资源,C等待A资源)