14

維持しなければならない大規模なレガシー システムがあります。コードベースはいたるところでスレッドを使用しており、それらのスレッドは多くの変更可能なデータを共有しています。私は知っています、悪いですね。とにかく、「アプリケーション全体を最初から書き直す」と答えないでください。そうしないと、投票します:-)コードベースでいくつかの静的分析ツールを実行しようとしましたが、どれも頻繁に発生するこのケースをキャッチしていないようですソースコードでは、複数のスレッドが、揮発性または同期としてマークされていない変数を読み書きしています。通常、これは「runFlag」タイプの変数で発生します。この例は、Effective Java 2nd edition ページ 260 にあります。

public class StopThread
{
    private static boolean stopRequested;
    public static void main(String[] args) throws InterruptedException
    {
        Thread backgroundThread = new Thread(new Runnable()
        {
            public void run()
            {
                int i = 0;
                while (!stopRequested)
                {
                    i++;
                }
            }
        });
        backgroundThread.start();
        Thread.sleep(1000);
        stopRequested = true;
    }
}

この例は、Sun JVM に「-server」起動パラメータが指定されている Windows/Linux では決して終了しません。では、これらの問題を (半) 自動で見つける方法はありますか、それとも完全にコード レビューに頼る必要がありますか?

4

5 に答える 5

6

Chris Grindstaff は、FindBugs、パート 2: カスタム ディテクタの作成という記事を書き、 BCELを使用して独自のルールを追加する方法を説明しています。(BCEL は唯一のバイトコード ライブラリではありませんが、FindBugs が使用するライブラリです。)

以下のコードは、メソッドが静的メソッドまたはフィールドにアクセスするすべてのケースを出力します。Runnableを実装する任意の型で実行できます。

public class StaticInvocationFinder extends EmptyVisitor {

    @Override
    public void visitMethod(Method obj) {
        System.out.println("==========================");
        System.out.println("method:" + obj.getName());

        Code code = obj.getCode();
        InstructionList instructions = new InstructionList(code.getCode());
        for (Instruction instruction : instructions.getInstructions()) {
            // static field or method
            if (Constants.INVOKESTATIC == instruction.getOpcode()) {
                if (instruction instanceof InvokeInstruction) {
                    InvokeInstruction invokeInstruction = (InvokeInstruction) instruction;
                    ConstantPoolGen cpg = new ConstantPoolGen(obj
                            .getConstantPool());
                    System.out.println("static access:"
                            + invokeInstruction.getMethodName(cpg));
                    System.out.println("      on type:"
                            + invokeInstruction.getReferenceType(cpg));
                }
            }
        }
        instructions.dispose();
    }

    public static void main(String[] args) throws Exception {
        JavaClass javaClass = Repository.lookupClass("StopThread$1");

        StaticInvocationFinder visitor = new StaticInvocationFinder();
        DescendingVisitor classWalker = new DescendingVisitor(javaClass,
                visitor);
        classWalker.visit();
    }

}

このコードは以下を発行します。

==========================
method:<init>
==========================
method:run
static access:access$0
      on type:StopThread

次に、タイプStopThreadをスキャンし、フィールドを見つけて、それがvolatileかどうかを確認することができます。

同期のチェックは可能ですが、複数の MONITOREXIT 条件が原因で複雑になる可能性があります。呼び出しスタックをたどることも難しいかもしれませんが、これは些細な問題ではありません。ただし、一貫して実装されていれば、バグパターンをチェックするのは比較的簡単だと思います。

BCELifierクラスが見つかるまで、BCEL はほとんど文書化されておらず、非常に複雑に見えます。クラスで実行すると、BCEL でクラスを構築する方法の Java ソースが吐き出されます。これをStopThreadで実行すると、 access$0合成アクセサーを生成するために次のようになります。

  private void createMethod_2() {
    InstructionList il = new InstructionList();
    MethodGen method = new MethodGen(ACC_STATIC | ACC_SYNTHETIC, Type.BOOLEAN, Type.NO_ARGS, new String[] {  }, "access$0", "StopThread", il, _cp);

    InstructionHandle ih_0 = il.append(_factory.createFieldAccess("StopThread", "stopRequested", Type.BOOLEAN, Constants.GETSTATIC));
    il.append(_factory.createReturn(Type.INT));
    method.setMaxStack();
    method.setMaxLocals();
    _cg.addMethod(method.getMethod());
    il.dispose();
  }
于 2008-10-09T19:22:53.937 に答える
2

FindBugsの最新バージョンは、@GuardedBy注釈が付けられたフィールドが適切なガードコード内でのみアクセスされることを確認しようとします。

于 2008-10-09T13:15:41.433 に答える
2

Coverity Thread Analyzer がその役割を果たしますが、これには非常にコストがかかります。IBM Multi-Thread Run-time Analysis Tool for Java はそれらを検出できるようですが、セットアップがやや難しいようです。これらは、適切な同期や揮発性なしに異なるスレッドからアクセスされた実際の変数を検出する動的分析ツールであるため、結果は静的分析よりも正確であり、静的分析では検出できない多くの問題を見つけることができます。

コードの大部分または少なくとも一部が適切に同期されている場合、FindBugs (またはその他の静的解析) の同時実行チェックを修正することも役立つ可能性があります。

于 2008-11-18T20:22:54.283 に答える
1

Coverity は、役に立つかもしれないいくつかの静的および動的分析ツールを作成します。

http://www.coverity.com/

于 2008-10-09T16:51:43.447 に答える
1

FindBugs とそれに基づく専門的なツールが最善の希望ですが、それらがコード内の同時実行の問題をすべて見つけてくれるとは期待しないでください。

状況がそれほど悪い場合は、人間の Java 並行処理の専門家による分析でツールを補う必要があります。

既存の、しかし変更されたコードベースの正確性を決定的に証明することはおそらく非現実的であるため、これは難しい問題です- 特に同時使用に直面した場合。

于 2008-10-09T12:35:08.083 に答える