りんごとバナナとエンジニア

エンジニア修行の記録

Javaでスレッドを作ってみる

JVMでは並行処理を実現するために、スレッドを作ることができる。
並列処理を使わなくてもよい処理を書いていたが、せっかくなので公式ドキュメントを見ながらスレッドを作ってみた。

スレッドを作る方法

スレッドを作るには2つの方法がある。ひとつはThreadクラスを継承してrunメソッドをオーバーライドすること。
公式に載ってた「初期値より大きい素数を計算する」コードを書いてみた。

class PrimeThread extends Thread {
    long minPrime;
    long nextNum;
    public PrimeThread(long minPrime) {
        this.nextNum = minPrime;
    }
    
    public void run() {
        System.out.println(calculate());
    }

    public long calculate() {
        do this.nextNum += 1; while (!isPrime(this.nextNum));
        return this.nextNum;
    }
    
    public boolean isPrime(long num) {
        if (num == 2) return true;
        if (num % 2 == 0) return false;
        for (int i = 3; i <= Math.sqrt(num); i += 2) {
            if (num % i == 0) return false;
        }
        return true;
    }   
}

public class SampleThread {
    public static void main(String[] args) {
        PrimeThread primeThread = new PrimeThread(140);
        primeThread.start();
    }
}

もうひとつは、Runnableインターフェースを実装すること。

class PrimeRun implements Runnable {
    long minPrime;
    long nextNum;
    public PrimeRun(long minPrime) {
        this.nextNum = minPrime;
    }
    
    public void run() {
        System.out.println(calculate());
    }

       \\ 以下同様なので中略
}

public class SampleThread {
    public static void main(String[] args) {
        PrimeRun primeRun = new PrimeRun(140);
        new Thread(primeRun).start();
    }
}

2つのやり方の違い

この2つのやり方はどう違うのかと思ったけれど、そこまで違いはなさそうだ。
Threadクラスのstartメソッドのコードは以下のようになっている。

public synchronized void start() {

        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

startメソッドは、C++で実装されたstart0メソッドを呼び出している。そしてstart0メソッドがrunメソッドを呼ぶのだが、このrunメソッドはオーバーライドされることが前提であり、親クラスの中の処理は何の意味も持たない。 ※ちなみにC++内でどんな処理をしているかは、以下の記事が詳しい。

Thread.javaのstart()はrun()をどのように呼ぶのか?(備忘録) - Qiita

一方Runnableインターフェースは、当然だが抽象メソッドとしてrunメソッドを定義しているだけだ。

public interface Runnable {
    public abstract void run();
}

どちらのやり方を使っても、結局runメソッドは全部自前のものを使うことになる。