什么是多线程中的竞争条件 - Java中的2个示例

Java 中的 竞态条件 是一种并发错误或问题,它是在您的程序中引入的,因为您的程序在多个线程 同时 并行执行 ,因为Java是一种多线程编程语言,因此在Java中竞争条件的风险更高要求清楚了解导致竞赛状况的原因以及如何避免这种情况 。 反正 竞争条件只是 é  危险或风险 在Java中使用多线程的呈现就像 在Java中的僵局 。 当两个线程 在没有正确同步的同一对象上操作并且操作彼此交错 时,就会出现 竞争条件 。 C  lassical   竞争条件的例子 是增加一个计数器,因为增量不是一个原子操作,可以进一步分为 三个步骤,如读,更新和写。 如果两个 线程同时 尝试增加计数,并且 由于一个线程的读取操作与另一个线程的更新操作交错而读取相同的值, 则一个线程覆盖其他线程完成的增量时 , 将丢失一个计数。 原子操作 不受竞态条件的限制,因为这些操作不能交错。 这也是 Java核心访谈期间 流行的多线程面试问题 。 在本文中,我们将看到 如何在Java中查找竞争条件   和两个 经常 导致 Java  竞争 条件的 示例代码模式 。


如何在Java中查找竞速条件


        用任何语言 查找竞争条件 都是最困难的工作,而Java也没有什么不同,不过由于Java代码的可读性 非常好,并且同步构造是通过代码审查找到竞争条件的良好定义堆。 由于竞赛条件的随机性, 通过 单元测试 发现竞赛条件 并不可靠。 因为竞赛条件只有一段时间才会 通过 单元 测试而不会面临任何竞争条件。 只有确定的方式来找到竞争条件是手动审查代码或使用代码 审查工具,它可以根据代码模式和Java中的同步使用提示潜在的竞争条件。 我仅仅依赖 于 代码审查 和尚未找到合适的 Java中暴露竞态条件的工具   。


Java中竞争条件的代码示例


乙 ASED我在Java同步体验,在这里我们使用synchronized关键字,我发现这两个代码的模式,即“ 检查并采取行动 ”和 “  读-修改-写  ”可以承受竞争条件,如果不妥善同步。 这两种情况都依赖于自然的假设,即单行 代码将是原子的并且在一次执行中执行是错误的,例如++不是原子的。  

“检查和行动”竞赛状态模式

在Java中,“检查和行为”竞争条件的典型例子是 Singleton Class的 getInsta  n  ce() 方法,请记住 我们已经讨论过的10个关于Java中的Singleton模式的访问问题的 一个问题 是“  如何编写线程安全Singleton在Java中  “。 getInstace()方法 首先检查实例是否为空,并初始化实例并返回给调用者。 Singleton的全部目的是getInstance  应该总是返回Singleton的同一个实例。 如果你 同时从两个线程中 调用 getInstance() 方法,那么它可能是   一个线程在空检查后初始化单例,另一个线程将_instance引用变量的值视为null(在 java中 很可能 ),尤其是如果对象需要较长时间来初始化并进入临界区,最终导致getInstance() 返回两个单独的实例辛格尔顿。 这可能不会总是发生,因为延迟的一小部分可能导致 在主内存中更新 _instance的值 。 这里是一个代码示例  

public Singleton getInstance(){
 
   
if(_instance == null){ 
       
     //race condition if two threads sees _instance= null
 
   
_instance = new Singleton();
 
   
}
 
   
}

解决“ 检查和交流 ”竞争条件的一个简单方法是同步关键字并强制执行锁定,这将使此操作成为原子 并确保块或方法仅由一个线程执行,并且操作结果将对所有线程都可见 已完成的 同步块 或从同步块中退出线程。  

阅读 - 修改 - 更新比赛条件

这是Java中引起竞争条件的另一种代码模式,经典示例是我们在 如何编写Java线程安全类时 讨论的非线程安全计数器 。这也是一个非常流行的多线程问题,他们要求您在并发代码中查找错误。 read-modify-update模式也是由于 非原子操作的 同步 正确 或两个单独的原子操作的组合 不一致 造成的。 考虑下面的代码  

if(!hashtable.contains(key)){
 
   
hashtable.put(key,value);
 
   
}

这里我们只将对象插入散列表中,如果它尚未存在的话。point是contains()和put()是原子的,但是这个代码仍然会 导致竞争条件,因为两个操作一起不是原子的。 考虑线程T1检查条件,如果 现在 阻塞,则进入内部 CPU从T1切换到线程T2,线程T2也检查条件,如果阻塞则进入线程T2。 现在我们在内部有两个线程,如果块 导致T1覆盖T2值,或者基于哪个线程有CPU执行,反之亦然。 为了 在Java中 解决这个竞争 条件, 你需要将这段代码包装在synchronized块中,这使得它们原子化在一起,因为 如果一个线程已经存在, 线程就不能进入 synchronized块。  

这些只是Java中竞争条件的一些例子,根据您的业务逻辑和代码会有很多例子。 寻找竞争条件的 最佳途径 是代码审查,但其难度很大,因为同时考虑并不自然,我们仍然假定代码 是 按顺序运行的。 P  如果JVM在没有适当的同步,以获得性能优势的代码重新排序,这通常发生在roblem可以变得更糟 重负载,这是最坏的下生产。 我还建议 在生产环境中 进行 负载测试 ,这种 测试 很多时候都有助于 揭示java中的竞争条件。 如果您在Java项目中遇到任何竞争条件,请分享。