読者です 読者をやめる 読者になる 読者になる

彼女からは、おいちゃんと呼ばれています

ウェブ技術や日々考えたことなどを綴っていきます

Jakarta Commons HttpClient によるSBI証券での自動売買(8) - 会社四季報の企業概要をデータベースへ格納編(マルチスレッド)


これまで会社四季報の企業概要データをデータベースへ格納するコードを書いてきましたが,今回の内容はそれらのコードをマルチスレッドで実行します。また併せて,シングルスレッドで実行したときとの処理時間の比較も行います。


というわけで目次は次のようになります。

  1. スレッドの作成(Thread クラスの拡張)
  2. マルチスレッドの実行
  3. 処理時間の比較(シングルスレッドとマルチスレッド)
  4. その他

スレッドの作成(Thread クラスの拡張)

詳しい説明は後述の参考書籍に譲りますが,スレッドの作成方法には次の2つがあります。

  • Thread クラスの拡張
  • Runnable インターフェイスクラスの実装


このうち,今回は前者の Thread クラスの拡張という方法を選択しました。あと,Log とか自作クラスも出てきますが,内容はこれまでのエントリーを参照してください。エントリーの最後に「関連エントリー」としてまとめておきます。

public class ImportCampanyThread extends Thread {
    /**
     * 企業コードの最小値
     */
    private String minCd;

    /**
     * 企業コードの最大値
     */
    private String maxCd;
    
    /**
     * ログ
     * 
     * 各スレッドの処理数や備考をまとめてからログに書き込むために,
     * ログクラスの受け渡しをする
     */
    private Log log;

    /**
     * 企業コードの最小値をセット
     */
    public void setMinCd(String cd) {
        this.minCd = cd;
    }

    /**
     * 企業コードの最大値をセット
     */
    public void setMaxCd(String cd) {
        this.maxCd = cd;
    }

    /**
     * ログをセット
     */
    public void setLog(Log log) {
        this.log = log;
    }
    /**
     * スレッドの処理
     */
    public void run() {
        SBI_Client sbi = new SBI_Client();

        try {
            sbi.login();

        } catch (Exception e) {
            e.printStackTrace();
            String message = "thread" + this.minCd + "_" + this.maxCd + " は,ログインに失敗しました。";
            System.out.println(message);
            this.log.addRemark(message);
            return;
        }

        int count = sbi.importCampany(this.minCd, this.maxCd);

        if (count == 0) {
            String message = "thread" + this.minCd + "_" + this.maxCd + " は,企業概要データの取込に失敗しました。";
            System.out.println(message);
            this.log.addRemark(message);
            return;

        } else {
            this.log.addCount(count);
        }
    }
}

マルチスレッドの実行

コピペばっかりしないでもう少しキレイに書くこともできるでしょうが,ここは理解しやすさ重視ということで。m(_ _)m

public class ImportCampany2 {
    /**
     * マルチスレッドで実行
     * 
     * 各スレッドにおける処理数や備考をまとめてログに書き込めるようにするため,
     * Log クラスを受け渡す
     */
    public static void main(String[] args) {

        // ログ
        Log log = new Log();
        log.setStartTime();
        System.out.println("開始時刻: " + log.getStartTime());
        System.out.println("------------------------------------------------------------");

        // データのクリア
        SBI_Client sbi = new SBI_Client();
        
        // マルチスレッドを行うため,データをクリアする処理を独立させて最初に実行
        try {
            sbi.truncateImpCampany();

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("企業概要データのクリアに失敗しました。");
            return;
        }

        // データの取込
        ImportCampanyThread thread1 = new ImportCampanyThread();
        thread1.setMinCd("1001");
        thread1.setMaxCd("2000");
        thread1.setLog(log);
        thread1.start();

        ImportCampanyThread thread2 = new ImportCampanyThread();
        thread2.setMinCd("2001");
        thread2.setMaxCd("3000");
        thread2.setLog(log);
        thread2.start();

        ImportCampanyThread thread3 = new ImportCampanyThread();
        thread3.setMinCd("3001");
        thread3.setMaxCd("4000");
        thread3.setLog(log);
        thread3.start();

        ImportCampanyThread thread4 = new ImportCampanyThread();
        thread4.setMinCd("4001");
        thread4.setMaxCd("5000");
        thread4.setLog(log);
        thread4.start();

        ImportCampanyThread thread5 = new ImportCampanyThread();
        thread5.setMinCd("5001");
        thread5.setMaxCd("6000");
        thread5.setLog(log);
        thread5.start();

        ImportCampanyThread thread6 = new ImportCampanyThread();
        thread6.setMinCd("6001");
        thread6.setMaxCd("7000");
        thread6.setLog(log);
        thread6.start();
        
        ImportCampanyThread thread7 = new ImportCampanyThread();
        thread7.setMinCd("7001");
        thread7.setMaxCd("8000");
        thread7.setLog(log);
        thread7.start();
        
        ImportCampanyThread thread8 = new ImportCampanyThread();
        thread8.setMinCd("8001");
        thread8.setMaxCd("9000");
        thread8.setLog(log);
        thread8.start();
        
        ImportCampanyThread thread9 = new ImportCampanyThread();
        thread9.setMinCd("9001");
        thread9.setMaxCd("9999");
        thread9.setLog(log);
        thread9.start();
        
        // すべてのスレッドが完了するまで待機
        try {
            thread1.join();
            thread2.join();
            thread3.join();
            thread4.join();
            thread5.join();
            thread6.join();
            thread7.join();
            thread8.join();
            thread9.join();

            System.out.println("------------------------------------------------------------");
            System.out.println("企業概要データを格納しました。");
            log.setEndTime();
            System.out.println("終了時刻: " + log.getEndTime());
            System.out.println("処理時間: " + log.getDiffTime());
    
            // ログを書き込む
            log.setWork("企業概要");
            log.setRemark("最小コード: 1001, 最大コード: 9999, HttpClient Java スレッド");
            log.writeLog();
        
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

処理時間の比較(シングルスレッドとマルチスレッド)

前回のエントリーで書いたシングルスレッドでの実行結果と比較すると,次のようになりました。同じ端末から実行したので,実行環境は簡単にしか書いてません。

実行環境
実行結果
シングルorマルチ 取り込んだ企業数*1 処理時間
シングルスレッド 3808 59分58秒
マルチスレッド 3805 19分06秒

マルチスレッドで実行すると,処理時間が 1/3 にまで短縮されました。やはり,Java のマルチスレッドは強力ですね。

参考書籍

独習Java 第4版

独習Java 第4版

この本を含め「独習」シリーズはサンプルコードがすばらしいです。自分の頭で考えながら読まざるを得ない,最適なコードが書かれています。これによって格段に理解は深まると思います。感謝。

参考サイト

マルチスレッドの仕組みについて考察が加えられています。

*1:取り込んだ企業数にわずかな違いが出ているのは,実行した時期が微妙に異なるためです。