マルチスレッドを作成する3つの方法

6627 ワード

javaではマルチスレッドを作成するための3つの方法を提供しています。前の二つは私たちのよくあるもので、第三はJDK 1.5の後に提供してくれます。次に、この三つのスレッドを作成する方法を詳しく見ます。
Thread類を継承する
第一の方式は、Thread類の書き方を継承しています。コードは以下の通りです。
        Thread thread = new Thread(){
            @Override
            public void run() {
                for(int i=0;i<10;i++){
                    System.out.println("          :"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
ここをカバーするのはrun方法です。start方法ではなく、start方法も絶対にカバーしないでください。なぜstartのカバー方法ができないですか?Threadのソースコードのstart方法を見てみます。
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        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 */
            }
        }
    }
注意この方法はロックをかける方法です。この方法の中で最も重要なコードは、start 0()である。次にstart 0()の方法を見に行きます。
    private native void start0();
それはnative方法です。このnativeのstart 0()方法は、起動スレッドを実現し、スタックメモリを申請し、run方法を実行し、スレッド状態を修正するという役割を果たしています。スレッド管理とスタックメモリ管理は、jvmが担当しています。もしあなたがstartメソッドをカバーしたら、スレッド管理とスタックメモリ管理の能力をキャンセルして、どうやってスレッドを起動しますか?でも、Threadのこのデザインはとても素晴らしいです。あなたはあなたの業務ロジックだけを注目してください。スレッドとスタックメモリの管理にはJVMがあればいいです。もしあなたの開発でstartメソッドをカバーしなければならないならば、super.startを呼び出すことを忘れないでください。そうでなければ、スレッドが起動できません。
Runnableインターフェースを実現
第二のスレッドを作成する方式は、Runnableインターフェースを実現することである。具体的なコードは下記をご覧ください。
       Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<10;i++){
                    System.out.println("          :"+Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread2.start();
この書き方はRunnableインターフェースを実現した書き方です。その原理は何ですか?私達はThreadのソースコードの中でrunの書き方を見にきました。
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
run法では、targetなら見られます!nullは、target.run()メソッドを呼び出します。このタージはどこから来ましたか?私達は引き続きThreadのソースコードを見ていますが、initの方法にはこのような言葉があります。
this.target = target;
次にinit()という方法はどこで呼び出されたのかを見ます。ソースを読むことによって、Threadの各構成関数でinitという方法を呼び出すことができます。また、このような構造関数があります。
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
見ましたよね。私たちはThreadを作成する時、Threadの構造関数を通して伝達されたRunnableの実現類です。スレッドが起動すると、runメソッドを呼び出して、runメソッドはまたRunnableを呼び出して、クラスのrunメソッドを実現します!!!
Callableインターフェースを実現
JDK 1.5の後にまた新しいスレッドを作成する方法を提供してくれました。Callable方法を実現します。具体的なコードは以下の通りです。
        FutureTask ft = new FutureTask(new Callable() {
            @Override
            public Integer call() throws Exception {
                int i = 0;
                for(;i<10;i++){
                    System.out.println("          :"+Thread.currentThread().getName());
                }
                return i;
            }
        });
        new Thread(ft).start();
上のコードでは、FutureTaskオブジェクトを作成する際に、Callableの匿名実装クラスをパラメータとしてFutureTaskのコンストラクタに伝達しましたが、スレッドが起動されると、作成されたFutureTaskのオブジェクトをパラメータとしてThreadのコンストラクタに伝達しました。ここで注意してください。この時は覆い隠すrun方法ではなく、コールという方法です。このFutureTaskとCallableという二つはいったい何の遊びなのか、みなさんは疑問に思うかもしれません。次の分析を行います。
FutureTask
FutureTaskのソースコードを読むことによって、Runnable Futureインターフェースが実現されました。Runnable FutureインターフェースはRunnableとFutureインターフェースを継承しました。注意:ここは二つのインターフェースを継承しました。JAVAには多くの継承がないのではないかという疑問があるかもしれません。はい、javaの中種類は多く受け継ぐことがないので、インターフェースに対してどれだけの継承がありますか?ここで一つのことが分かります。FutureTaskはRunnableインターフェースの一つの実現類です。ここに来たら何か分かりますか?
public class FutureTask implements RunnableFuture
public interface RunnableFuture extends Runnable, Future 
分からないなら、第二のスレッドを作る方式とその原理を何回も見ましょう。次にFutureTaskという種類のrun方法を見てみます。
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
上のこの方法のコードは全部貼っていません。主な部分だけ貼ってきました。この方法では、このような2つの文のCallable c=callableを発見します。result=c.cal();この二つの言葉が鍵です!!!ソースを読むと、私たちはソースコードのcallbleを発見します。これは先ほどFutureTaskに届いたCallableの実現タイプです。
    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
c.callそれはコールのcallable実現類のコール方法ではないですか?!!ここに来てやっと分かりました。FutureTaskの他の方法に興味がある人は研究を続けてもいいです。
Callable
Callableというインターフェースには一つのcall方法しかありません。締め括りをつける
実際のコードでは、作成スレッドがより多く使われているのを見ました。それはjavaにおけるインターフェース向けプログラミングの思想により適合しています。最後に問題を出して、みんなを試験します。次のコードの運行結果を教えてください。
      new Thread(new Runnable() {
          @Override
          public void run() {
              System.out.println("Runnable      :");
          }
      }){
          @Override
          public void run() {
              System.out.println("  Thread   :");
          }
      }.start();