使用findbugs或其他分析工具检测竞争条件

时间:2011-04-06 19:09:43

标签: java thread-safety findbugs

下面的bean不是线程安全的:方法addIfNotExist不同步,因此由于竞争条件,相同的术语可能会被添加两次。我使用JCIP注释@ThreadSafe注释该类,希望FindBugs发现该实现不是线程安全的,并将其标记为错误,但事实并非如此。是否有任何工具可以识别代码库中的这些类型的错误?

方法addIfNotExist和isExist应该同步以使这个bean线程安全。 isExist方法是否也应该同步?

package com.test;

import java.util.ArrayList;

import java.util.Collection;

import net.jcip.annotations.GuardedBy;

import net.jcip.annotations.ThreadSafe;

@ThreadSafe

public class Dictionary {

    @GuardedBy("this")
    public Collection<String> terms = new ArrayList<String>();

    public void addIfNotExist(final String input) {
        if (!this.terms.contains(input)) {
            this.terms.add(input);
        }
    }

    public boolean isExist(final String input){
        return this.terms.contains(input);
    }

    public void remove(final String input){
        this.terms.remove(input);
    }
}

3 个答案:

答案 0 :(得分:4)

编写具有任何复杂程度的安全多线程代码是非常difficult:这种类型的锁定(使用监视器)充满了各种间歇性竞争条件,死锁和常常逃避的活锁问题检测到升级到生产系统;如果可以,请考虑使用message passingsoftware transactional memorypersistent data structures代替。

FindBugs(或者实际上任何静态分析工具)只能在检测非线程安全代码方面走得太远:根据它们的定义,竞争条件是时间敏感的 - 它们需要很多执行运行来显示,所以静态分析失败了这方面,因为他们不运行任何代码,只查找公共代码签名。恕我直言,检测问题的最佳方法是:

  • 第二双眼睛 - 与熟悉代码的同行进行严格的代码审查 - 一直在寻找对原作者来说不是很明显的错误。

  • 持续整合&amp;详尽的自动化测试,可在各种硬件上运行多线程,并且无情地调查任何“间歇性”测试失败。

在回答第二个问题时,是的,所有引用terms的方法都应该由同步监视器保护,无论它是写操作还是读操作;考虑当线程A在没有同步时调用remove("BOB")时线程A调用isExists("BOB")会发生什么 - 线程A将压缩数组列表,而线程B将尝试遍历它。

充其量,您将无法确定isExists("BOB")的结果,但B完全可能会间歇性地抛出IndexOutOfBounds异常,因为数组的大小可能已经改变(即缩小因为它正在被遍历。

同步,虽然您仍然无法确定调用的顺序(由于调度的非确定性),但至少可以保证terms上的操作将是原子的 - 也就是说,当前线程正在运行时,它们不会被其他东西改变。

答案 1 :(得分:3)

您可以在运行时(在自动单元测试或集成测试期间或您有什么用途)使用帮助查找线程问题:IBM ConTest (Concurrency Testing)

ConTest说明: “ConTest技术具有创新性和反直觉性。具体来说,ConTest系统地,透明地安排程序线程的执行,以便可能包含竞争条件,死锁和其他间歇性错误的程序方案 - 统称为同步问题 - 是这样做,ConTest极大地提高了测试质量,降低了开发费用,因为在测试过程的早期就发现了错误。“

答案 2 :(得分:1)

要找到这种错误同步的代码块,我使用以下算法:

使用检测记录所有字段修改的线程。如果一个字段被多个线程修改而没有同步,我发现了一个数据竞争。

我在http://vmlens.com中实现了这个算法,这是一个在java程序中查找数据竞争的动态工具。