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

我对社区的问题是:

竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?


当前回答

当两个或多个线程可以访问共享数据,并且它们试图同时更改数据时,就会发生竞态条件。因为线程调度算法可以在任何时候在线程之间交换,所以您不知道线程将尝试访问共享数据的顺序。因此,数据更改的结果依赖于线程调度算法,即两个线程都在“竞相”访问/更改数据。

当一个线程执行“检查-然后-行动”时,问题经常发生。“check”如果值是X,那么“act”做一些取决于值是X的事情),另一个线程在“check”和“act”之间对值做一些事情。例句:

if (x == 5) // The "Check"
{
   y = x * 2; // The "Act"

   // If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
   // y will not be equal to 10.
}

这一点是,y可以是10,也可以是任何值,这取决于在检查和执行之间是否有另一个线程改变了x。你根本不知道。

为了防止竞争条件的发生,您通常会在共享数据周围放置一个锁,以确保一次只有一个线程可以访问数据。这意味着:

// Obtain lock for x
if (x == 5)
{
   y = x * 2; // Now, nothing can change x until the lock is released. 
              // Therefore y = 10
}
// release lock for x

其他回答

一个有点规范的定义是“当两个线程同时访问内存中的同一个位置,并且至少有一次访问是写操作。”在这种情况下,“reader”线程可能获得旧值或新值,这取决于哪个线程“赢得了比赛”。这并不总是一个bug——事实上,一些非常复杂的低级算法会故意这样做——但通常应该避免。@Steve Gury的例子很好地说明了这可能是个问题。

微软实际上已经发布了一篇关于竞态条件和死锁的非常详细的文章。最概括的摘要是标题段:

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.

什么是竞态条件?

You are planning to go to a movie at 5 pm. You inquire about the availability of the tickets at 4 pm. The representative says that they are available. You relax and reach the ticket window 5 minutes before the show. I'm sure you can guess what happens: it's a full house. The problem here was in the duration between the check and the action. You inquired at 4 and acted at 5. In the meantime, someone else grabbed the tickets. That's a race condition - specifically a "check-then-act" scenario of race conditions.

你如何发现它们?

宗教代码审查多线程单元测试没有捷径。在此基础上出现了一些Eclipse插件,但还没有稳定的插件。

你如何处理和预防它们?

最好的方法是创建无副作用和无状态的函数,尽可能使用不可变函数。但这并不总是可能的。使用java。util。concurrent。原子的、并发的数据结构、适当的同步和基于参与者的并发将有所帮助。

并发性的最佳资源是JCIP。你也可以在这里得到更多关于上述解释的细节。

如果你使用“原子”类,你可以防止竞争条件。原因是线程没有分开操作get和set,示例如下:

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

因此,你将有7在链接“ai”。 虽然你做了两个操作,但这两个操作都确认了同一个线程,没有其他线程会干涉这,这意味着没有竞争条件!

竞态条件是并发编程中的一种情况,其中两个并发线程或进程争夺资源,最终状态取决于谁先获得资源。