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

dsa*_*ish 8 java findbugs thread-safety

下面的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

rhu*_*rhu 4

编写具有任何复杂程度的安全多线程代码都非常困难:这种类型的锁定(使用监视器)充满了各种间歇性竞争条件、死锁和活锁问题,这些问题通常会在升级到生产环境时逃避检测系统;如果可以的话,请考虑使用消息传递软件事务内存持久数据结构

FindBugs(或者实际上任何静态分析工具)在检测非线程安全代码方面只能走这么远:根据其定义,竞争条件是时间敏感的 - 它们需要多次执行才能显现,因此静态分析在这方面失败,因为他们不运行任何代码,只寻找常见的代码签名。恕我直言,检测问题的最好方法是:

  • 第二双眼睛——与熟悉代码的同行一起进行严格的代码审查——有助于发现对原作者来说并不明显的错误。

  • 持续集成和详尽的自动化测试,在各种硬件上执行多线程性,并无情地调查任何“间歇性”测试失败。

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

充其量,您将无法确定 的结果isExists("BOB"),但 B 完全有可能间歇性地抛出IndexOutOfBounds异常,因为数组的大小可能在遍历时发生变化(即缩小)。

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