JavaRush /Java Blog /Random-JA /スレッドで Java を台無しにすることはできません: パート I - スレッド
Viacheslav
レベル 3

スレッドで Java を台無しにすることはできません: パート I - スレッド

Random-JA グループに公開済み

導入

マルチスレッドは、初期の頃から Java に組み込まれていました。それでは、マルチスレッドとは何なのかを簡単に見てみましょう。 Oracle の公式レッスン「レッスン: 「Hello World!」アプリケーションスレッドで Java を破壊することはできません: パート I - スレッド - 1」を出発点として見てみましょう。Hello World アプリケーションのコードを次のように少し変更してみましょう。
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsプログラムの開始時に渡される入力パラメータの配列です。このコードを、クラス名と拡張子 に一致する名前のファイルに保存しましょう.javajavacユーティリティを使用してコンパイルしましょう。javac HelloWorldApp.java その後、いくつかのパラメータを使用してコードを呼び出します。たとえば、Roger:java HelloWorldApp Roger スレッドで Java を破壊することはできません: パート I - スレッド - 2私たちのコードには重大な欠陥があります。引数を渡さない場合 (つまり、java HelloWorldApp を実行するだけ)、エラーが発生します。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
という名前のスレッドで例外 (つまり、エラー) が発生しましたmain。Java にはある種のスレッドがあることがわかりました。ここから私たちの旅が始まります。

Javaとスレッド

スレッドとは何かを理解するには、Java アプリケーションがどのように起動されるかを理解する必要があります。コードを次のように変更してみましょう。
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
それでは、javac を使用して再度コンパイルしてみましょう。次に、便宜上、Java コードを別のウィンドウで実行します。Windows では、次のようにこれを行うことができますstart java HelloWorldApp。ここで、jpsユーティリティを使用して、Java がどのような情報を表示するかを見てみましょう。 スレッドで Java を破壊することはできません: パート I - スレッド - 3最初の数字は PID またはプロセス ID、プロセス識別子です。プロセスとは何ですか?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
プロセスの助けを借りて、さまざまなプログラムの実行は互いに分離されます。各アプリケーションは、他のプログラムに干渉することなく独自のメモリ領域を使用します。記事「https://habr.com/post/164487/」をさらに詳しく読むことをお勧めします。プロセスはスレッドなしでは存在できないため、プロセスが存在する場合は、そのプロセス内に少なくとも 1 つのスレッドが存在します。Java ではどのようにしてこれが起こるのでしょうか? Java プログラムを実行するとき、その実行はmain. 私たちはプログラムに入るようなものなので、この特別な方法はmainエントリーポイント、または「エントリーポイント」と呼ばれます。このメソッドは、main常にpublic static voidJava 仮想マシン (JVM) がプログラムの実行を開始できるようにする必要があります。詳細については、「Java の main メソッドが静的であるのはなぜですか? 」を参照してください。Java ランチャー (java.exe または javaw.exe) は単純なアプリケーション (単純な C アプリケーション) であることがわかります。実際には JVM であるさまざまな DLL をロードします。Java ランチャーは、特定の Java Native Interface (JNI) 呼び出しのセットを作成します。JNI は、Java 仮想マシンの世界と C++ の世界の橋渡しをするメカニズムです。ランチャーは JVM ではなく、そのローダーであることがわかりました。JVM を起動するために実行する正しいコマンドを認識します。JNI 呼び出しを使用して必要なすべての環境を編成する方法を知っています。この環境の編成には、通常 と呼ばれるメイン スレッドの作成も含まれますmain。Java プロセス内にどのスレッドが存在するかをより明確に確認するには、JDK に含まれるjvisualvmプログラムを使用します。プロセスの pid がわかれば、そのデータをすぐに開くことができます。jvisualvm --openpid айдипроцесса スレッドで Java を破壊することはできません: パート I - スレッド - 4興味深いことに、各スレッドには、プロセスに割り当てられたメモリ内に独自の個別の領域があります。このメモリ構造はスタックと呼ばれます。スタックはフレームで構成されます。フレームはメソッドを呼び出すポイント、つまり実行ポイントです。フレームは StackTraceElement として表すこともできます ( StackTraceElementの Java API を参照)。各スレッドに割り当てられるメモリの詳細については、こちらをご覧ください。Java APIを見てThread という単語を検索すると、 java.lang.Threadクラスがあることがわかります。Java でストリームを表すのはこのクラスであり、これを使用して作業する必要があります。 スレッドで Java を破壊することはできません: パート I - スレッド - 5

java.lang.スレッド

Java のスレッドは、クラスのインスタンスとして表されますjava.lang.Thread。Java の Thread クラスのインスタンスはそれ自体がスレッドではないことをすぐに理解する価値があります。これは、JVM とオペレーティング システムによって管理される低レベル スレッド用の API の一種にすぎません。Java ランチャーを使用して JVM を起動すると、名前付きのメイン スレッドmainとさらにいくつかのサービス スレッドが作成されます。Thread クラスの JavaDoc に記載されているように、 When a Java Virtual Machine starts up, there is usually a single non-daemon thread スレッドにはデーモンと非デーモンの 2 種類があります。デーモン スレッドは、バックグラウンドで何らかの作業を実行するバックグラウンド スレッド (サービス スレッド) です。この興味深い用語は「マクスウェルの悪魔」への言及であり、ウィキペディアの「悪魔」に関する記事で詳細を読むことができます。ドキュメントに記載されているように、JVM は次の状態になるまでプログラム (プロセス) を実行し続けます。
  • Runtime.exitメソッドが呼び出されない
  • デーモン以外のすべてのスレッドが作業を完了しました (エラーも例外もスローされませんでした)
したがって、重要な点は、実行中のどのコマンドでもデーモン スレッドを終了できるということです。したがって、それらのデータの完全性は保証されません。したがって、デーモン スレッドは一部のサービス タスクに適しています。たとえば、Java には、ファイナライズ メソッドの処理を担当するスレッドや、ガベージ コレクター (GC) に関連するスレッドがあります。各スレッドは何らかのグループ ( ThreadGroup ) に属します。そして、グループが互いに入り込んで、何らかの階層や構造を形成することがあります。
public static void main(String []args){
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
グループを使用すると、フローの管理を合理化し、フローを追跡できます。グループに加えて、スレッドには独自の例外ハンドラーがあります。例を見てみましょう:
public static void main(String []args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
ゼロによる除算はエラーを引き起こし、ハンドラーによって捕捉されます。ハンドラーを自分で指定しない場合は、デフォルトのハンドラー実装が機能し、StdError にエラー スタックが表示されます。詳細については、レビューhttp://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/を参照してください。さらに、スレッドには優先順位があります。優先順位について詳しくは、記事「マルチスレッドにおける Java スレッドの優先順位」。

スレッドの作成

ドキュメントに記載されているように、スレッドを作成するには 2 つの方法があります。1つ目は後継者を作ることです。例えば:
public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    }

    public static void main(String []args){
        Thread thread = new MyThread();
        thread.start();
    }
}
ご覧のとおり、タスクはメソッドで起動されrun、スレッドはメソッドで起動されますstart。彼らを混乱させるべきではありません。なぜなら... メソッドをrun直接実行すると、新しいスレッドは開始されません。startこれは、JVM に新しいスレッドの作成を要求するメソッドです。Thread の子孫を使用するオプションは、クラス階層に Thread を含めるので不適切です。2 番目の欠点は、「単独責任」の原則に確実に違反し始めていることです。私たちのクラスは、スレッドの管理と、このスレッドで実行する必要があるタスクの両方を同時に担当します。どちらが正しい?run答えは、オーバーライドする メソッドそのものにあります。
public void run() {
	if (target != null) {
		target.run();
	}
}
以下に、クラスのインスタンスを作成するときに Thread に渡すことができるtargetいくつかの を示します。java.lang.Runnableしたがって、次のようにすることができます。
public class HelloWorld{
    public static void main(String []args){
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
RunnableこれはJava 1.8 以降の関数インターフェース でもあります。これにより、スレッドのタスク コードをさらに美しく記述できるようになります。
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

合計

したがって、この話から、ストリームとは何か、ストリームがどのように存在し、ストリームを使用してどのような基本操作を実行できるかが明確になることを願っています。次のパートでは、スレッドがどのように相互作用するか、またスレッドのライフサイクルが何であるかを理解する価値があります。#ヴィアチェスラフ
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION