9

Javaでのマルチスレッド化は、run()を定義し、start()を呼び出すことによって行われます。

オペレーティングシステムルーチンを介してスレッドを起動するネイティブメソッドへのデリゲートを開始し、この新しく生成されたスレッド内からrun()が呼び出されます。

スタンドアロンアプリケーションを起動すると、main()を実行するためのメインスレッドが自動的に作成されます。

ここで、このコードについて考えてみましょう-

public class Test extends Thread {
    public static void main(String[] args) throws Exception {
        new Thread(new Test()).start();
        throw new RuntimeException("Exception from main thread");
    }
    public void run() {
        throw new RuntimeException("Exception from child thread");
    }
}

これは出力です-

java.lang.RuntimeException: Exception from child thread
    at com.Test.run(Test.java:11)
    at java.lang.Thread.run(Thread.java:662)
java.lang.RuntimeException: Exception from main thread
    at com.Test.main(Test.java:8)

main()メソッドがスレッドを介して起動された場合、なぜrun()が呼び出し階層の最上位に表示されないのですか?

Runnableを実装せずにメインスレッドを生成するにはどうすればよいですか?

4

4 に答える 4

7

main()メソッドがスレッドを介して起動された場合、なぜrun()が呼び出し階層の最上位に表示されないのですか?

他の人が述べているように、これは「メイン」スレッドが特別であるためです。これは、標準Threadのクラスメカニズムではなく、Javaブートストラップコードを介して起動されます。はpublic static void main(String[] args)常にネイティブコードのメインスレッドによって実行されます。

もう1つの説明は、実際にはrun()メソッドがあるかもしれませんが、スタックフレームを構築する方法は、ユーザーを混乱させないために意図的にそれを隠しているということです。たとえば、new Thread(new Test())thenを実行しているので、Testクラスは実際には。target内のフィールドThreadです。バックグラウンドThreadが開始されると、実際には次Thread.run()のコードを持つを呼び出します。

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

しかし、スタックフレームにThread.run()メソッドがあるはずなのに、そこにメソッドが表示されることはありません。run()ユーザーがスーパークラスでメソッドオーバーライドした場合、メソッドはスタックフレームにありThreadます。スタックフレーム出力を改善するために、JDKによって削除される可能性があります。

Javaでのマルチスレッド化は、run()を定義し、start()を呼び出すことによって行われます。

これは正しいですが、後世のために、コードに問題があることを認識することが重要だと思いました。Testクラスは拡張する必要はありませThreadが、代わりにを実装する必要がありますRunnableThreadを実装しているので動作しますRunnable

コードを実装Runnableして次のように変更する必要があります。

public class Test implements Runnable {
    public static void main(String[] args) throws Exception {
        new Thread(new Test()).start();
        throw new RuntimeException("Exception from main thread");
    }
    public void run() {
        throw new RuntimeException("Exception from child thread");
    }
}

Threadまたは、スレッドの開始方法を次のように拡張して変更します。上記のRunnableパターンは、Testスレッドが必要に応じて別のクラスを拡張できるようにするために推奨されます。

public class Test extends Thread {
    public static void main(String[] args) throws Exception {
        new Test().start();
        throw new RuntimeException("Exception from main thread");
    }
    @Override
    public void run() {
        throw new RuntimeException("Exception from child thread");
    }
}

何でこれが大切ですか?現在のコードは実際には2Threadつのオブジェクトをインスタンス化していますが、そのうちの1つだけがstart()編集され、バックグラウンドとして実行されていますThread。次のようなバグが発生する可能性があります。

public class Test extends Thread {
    public static void main(String[] args) throws Exception {
        Test test = new Test(); 
        new Thread(test).start();
        // this is not interrupting the background thread
        test.interrupt();
于 2012-05-26T09:37:47.960 に答える
4

JVMの内部については調べていませんが、JVMはメインスレッドをインスタンス化してメインメソッドを実行しますが、従来のJavaクラスとメソッドを経由せずに、ネイティブコードを直接呼び出すことによってこのメインスレッドを実行します。スレッドを開始します。

于 2012-05-26T06:22:12.030 に答える
3

mainメソッドはJVMによって別のスレッドで開始されます。これは子スレッドの親スレッドであるため、呼び出し階層の最上位に子スレッドが表示されません。

したがって、あなたの場合、JVMはプログラムを開始するスレッドを作成し、それはスレッドも拡張します。

次に、メインメソッドでstartと呼ばれるクラスの新しいインスタンスを作成しました。これにより、プログラムを起動するためにJVMによって開始されたスレッドの子である新しいスレッドが開始されます。

mainメソッドはスタンドアロンJavaプログラムの開始点であるため、別のスレッドで起動するのはJVMの責任であり、コードを記述しないでください。

メインメソッドを呼び出してプログラムを起動する場合、JVMはスレッドである必要はなく、Runnableを実装する必要もありませんが、これは標準的な手順です。

内部Java仮想マシンからの説明

アプリケーションの初期クラスのmain()メソッドは、そのアプリケーションの初期スレッドの開始点として機能します。最初のスレッドは、他のスレッドを起動することができます。

Java仮想マシン内では、スレッドにはデーモンと非デーモンの2つの種類があります。デーモンスレッドは通常、ガベージコレクションを実行するスレッドなど、仮想マシン自体によって使用されるスレッドです。ただし、アプリケーションは、作成するすべてのスレッドをデーモンスレッドとしてマークできます。アプリケーションの最初のスレッド(main()で始まるスレッド)は、デーモン以外のスレッドです。

デーモン以外のスレッドがまだ実行されている限り、Javaアプリケーションは実行を継続します(仮想マシンインスタンスは存続し続けます)。Javaアプリケーションのすべての非デーモンスレッドが終了すると、仮想マシンインスタンスは終了します。セキュリティマネージャによって許可されている場合、アプリケーションは、RuntimeクラスまたはSystemクラスのexit()メソッドを呼び出すことによって独自の停止を引き起こすこともできます。

呼び出し階層はユーザーによって管理されておらず、基盤となるスレッドスケジューラによって管理されています。

たとえば、自分のマシンで同じコードを実行すると、これが出力になります。

Exception in thread "main" java.lang.RuntimeException: Exception from main thread
    at TestThread.main(TestThread.java:6)
Exception in thread "Thread-1" java.lang.RuntimeException: Exception from child thread
    at TestThread.run(TestThread.java:9)
    at java.lang.Thread.run(Thread.java:662)

したがって、例を実行すると、スケジューラーはメインの前に最初に子スレッドを解放することを選択します。

@JB Nizetに加えて、プログラムがどのように呼び出されるか、またはスレッドのライフサイクルがどのように実装されるかは、基盤となるOSとハードウェアによって異なります。

単一の実装の詳細が完全な答えを提供することはありません。各実装は異なります。

于 2012-05-26T05:49:53.323 に答える
0

おそらくそれは意味論的な議論ですが、スレッドとプロセスは同義ではありません。

プロセスはオペレーティングシステムによって起動され、独自のプライベートコードページ(コンパイルされたコードセット)を持っています。スレッドはプログラム内部から起動され、最初はその親のコードページ(コンパイルされたコードセット)を共有します。

JVMが内部でスレッドを使用してmainメソッドを実行することは、環境の実装の詳細ですが、Java言語の表現ではありません。スレッドがスタックフレームでレポートする場合、mainそのメインサービングスレッドにはコンパイルユニットがないため(JVMの内部にあるため)、ソースコードの位置を参照するアーキテクチャは不可能です。

于 2013-07-30T18:54:11.667 に答える