Junitがマルチスレッドをテストするときに発生する問題

6268 ワード

問題の発生この問題は私が支付宝の自動帳簿合わせ機能をする時発見したので、支付宝の帳簿合わせインタフェースのダウンロードする帳簿合わせはzip圧縮ファイルの形式で帰ってくるので、自動帳簿合わせ機能を実現するには私が支付宝の帳簿合わせインタフェースを呼び出してzipファイルをダウンロードし終わる前にまず1つのスレッドを起動してzipファイルを保存するフォルダを監視して、このフォルダにzipファイルが生成する時、zipファイルを解凍するには、解凍メソッドが自動的にトリガーされます.方法が书いた后に私はjunitフレームワークでテストして、ずっとzipファイルを解冻することに成功していないことを発见して、私は出力ログを通じて発见して、“解冻”の方法に転勤することがあって、しかし“解冻”の方法の実行が完成する前にプログラムはすでに终わって、それから私はmain関数の方式でjunitに取って代わってテストを行って、プログラムが自动的に解冻することができることを発见して、これにより、junitはマルチスレッドテストをサポートしていない(この説は完全に正確ではない.ネット上にはjunitでマルチスレッドをテストする大神がいるようだが、具体的にどのように実現するかはまだ深く研究されていない.原生のjunitがマルチスレッドテストをサポートしていないことを確定しているだけだ)
私自身が書いた支付宝の自動帳簿機能に関するコードが多く、論理が複雑で、この問題を説明するのにあまり適していないので、ネット上で別の例を探して参考にしました.例はhttps://www.cnblogs.com/yanphet/p/5774291.html
/**
 * @Title: TestDoWork.java
 * @Describe:
 * @author: Mr.Yanphet
 * @Email: [email protected]
 * @date: 2016 8 15   5:50:03
 * @version: 1.0
 */
public class TestDoWork {

    class DoWork implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                long milliSecond = System.currentTimeMillis();
                System.out.println("i=" + i + ",milliSecond=" + milliSecond);//  
            }
        }

    }

    @Test
    public void test() {
        DoWork dw = new DoWork();
        Thread t = new Thread(dw);
        t.start();
    }

}

出力結果
i=751,milliSecond=1471257586416
i=752,milliSecond=1471257586416
i=753,milliSecond=1471257586416
i=754,milliSecond=1471257586416
i=755,milliSecond=1471257586416
i=756,milliSecond=1471257586416
i=757,milliSecond=1471257586416
i=758,milliSecond=1471257586416

結果から,759回ループした後に出力されず,サブスレッドがまだタスクを終了していないことを示し,プログラム全体が強制的に終了した.現象が分かった以上、なぜこのような現象が発生したのか、Junit 4 TestRunnerのソースコードの一部を貼ればわかります
public static final int SUCCESS_EXIT = 0;
public static final int FAILURE_EXIT = 1;
public static final int EXCEPTION_EXIT = 2;

public static void main(String args[]) {
    TestRunner aTestRunner = new TestRunner();
    try {
        TestResult r = aTestRunner.start(args);
        if (!r.wasSuccessful())
            System.exit(FAILURE_EXIT);
        System.exit(SUCCESS_EXIT);
    } catch (Exception e) {
        System.err.println(e.getMessage());
        System.exit(EXCEPTION_EXIT);
    } 
}

参考までにTestResultのソースコードの一部を貼り付けます
protected  List    fFailures
protected  List    fErrors

public synchronized boolean wasSuccessful() {
    return failureCount() == 0 && errorCount() == 0;
}

public synchronized int errorCount() {
    return fErrors.size();
}

public synchronized int failureCount() {
    return fFailures.size();
}

TestRunnerでは、単一スレッドの場合、テストメインスレッドの実行が終了すると、チューブスレッドが終了するか否かを問わず、TestResultのwassSuccessfulメソッドをコールバックし、結果が成功するか失敗するかを判断し、対応するSystemを呼び出すことがわかる.exit()メソッド.この方法は現在稼働中のjava仮想マシンを終了するために使用されていることはよく知られていますが、jvmは自分では保証できません.だから、サブスレッドも申し訳ありません.解決策:1簡単にメインスレッドをしばらく休眠させ、サブスレッドを終了させることができます.しかし、この方法の弊害は、サブスレッドの実行時間が分からないため、顔==を見る必要があることです.  Thread.sleep(); 2 CountDownLatchツールクラスを使用して、サブスレッドの実行が終了するか、ブロックがタイムアウトするまで、メインスレッドをブロックします.この方法は最初の方法よりも優れています.  countDownLatch.await(5, TimeUnit.MINUTES); 他にも方法がありますが、筆者は多くの大神が自分で書いたJunitがマルチスレッドをサポートしているのを見て、興味のある読者は自分で娘を過ごしています...