CodeGym /Java Blog /ランダム /Java と Thread クラスを組み合わせるとさらに効果的です。パート I — 実行スレッド
John Squirrels
レベル 41
San Francisco

Java と Thread クラスを組み合わせるとさらに効果的です。パート I — 実行スレッド

ランダム グループに公開済み

序章

マルチスレッドは最初から Java に組み込まれていました。それでは、マルチスレッドと呼ばれるものについて簡単に見てみましょう。 Oracle の公式レッスン「レッスン: 「Hello World!」アプリケーションJava と Thread クラスを組み合わせるとさらに効果的です。 パート 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 と Thread クラスを組み合わせるとさらに効果的です。 パート 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 と Thread クラスを組み合わせるとさらに効果的です。 パート I — 実行スレッド - 3最初の数字は PID またはプロセス ID です。プロセスとは何ですか?

A process is a combination of code and data sharing a common virtual address space.
プロセスでは、さまざまなプログラムが実行中に互いに分離されます。各アプリケーションは、他のプログラムに干渉することなく、メモリ内の独自の領域を使用します。詳細については、このチュートリアル「プロセスとスレッド」を読むことをお勧めします。プロセスはスレッドなしでは存在できないため、プロセスが存在する場合、そのプロセスには少なくとも 1 つのスレッドがあります。しかし、これは Java ではどのようにして実現されるのでしょうか? Java プログラムを開始すると、mainメソッドから実行が始まります。あたかもプログラムに足を踏み入れるようなので、この特別なmain方法をエントリーポイントと呼びます。mainJava 仮想マシン (JVM) がプログラムの実行を開始できるように、メソッドは常に「public static void」である必要があります。詳細については、「なぜ Java メイン メソッドは静的ですか?」を参照してください。。Java ランチャー (java.exe または javaw.exe) は単純な C アプリケーションであることがわかりました。実際に JVM を構成するさまざまな DLL をロードします。Java ランチャーは、特定の Java Native Interface (JNI) 呼び出しのセットを作成します。JNI は、Java 仮想マシンの世界と C++ の世界を接続するためのメカニズムです。したがって、ランチャーは JVM 自体ではなく、JVM をロードするメカニズムです。JVM を起動するために実行すべき正しいコマンドを認識しています。JNI 呼び出しを使用して必要な環境をセットアップする方法を知っています。この環境のセットアップには、もちろん「メイン」と呼ばれるメイン スレッドの作成が含まれます。Java プロセスにどのスレッドが存在するかをよりわかりやすく説明するために、jvisualvm を使用しますこのツールは JDK に含まれています。プロセスの pid がわかれば、そのプロセスに関する情報をすぐに確認できます。jvisualvm --openpid <process id> Java と Thread クラスを組み合わせるとさらに効果的です。 パート I — 実行スレッド - 4興味深いことに、各スレッドには、プロセスに割り当てられたメモリ内に独自の個別の領域があります。このメモリ構造はスタックと呼ばれます。スタックはフレームで構成されます。フレームはメソッドのアクティブ化 (未完了のメソッド呼び出し) を表します。フレームは StackTraceElement として表すこともできます ( StackTraceElementの Java API を参照)。各スレッドに割り当てられるメモリの詳細については、「Java (JVM) が各スレッドにスタックを割り当てる方法」の説明を参照してください。Java APIを見て「Thread」という単語を検索すると、java.lang.Threadが見つかります。クラス。これは Java のスレッドを表すクラスであり、これを操作する必要があります。 Java と Thread クラスを組み合わせるとさらに効果的です。 パート I — 実行スレッド - 5

java.lang.スレッド

Java では、スレッドはクラスのインスタンスによって表されますjava.lang.Thread。Thread クラスのインスタンス自体は実行スレッドではないことがすぐに理解できるはずです。これは、JVM とオペレーティング システムによって管理される低レベル スレッド用の API の一種にすぎません。Java ランチャーを使用して JVM を起動すると、main「メイン」と呼ばれるスレッドとその他のいくつかのハウスキーピング スレッドが作成されます。Thread クラスの JavaDoc に記載されているとおりです When a Java Virtual Machine starts up, there is usually a single non-daemon thread。スレッドにはデーモンと非デーモンの 2 種類があります。デーモン スレッドは、バックグラウンドで何らかの作業を実行するバックグラウンド (ハウスキーピング) スレッドです。「デーモン」という言葉はマクスウェルの悪魔を指します。詳細については、このWikipedia の記事を参照してください。ドキュメントに記載されているように、JVM は次の状態になるまでプログラム (プロセス) を実行し続けます。
  • Runtime.exit ()メソッドが呼び出されます
  • すべての非デーモン スレッドは (エラーや例外がスローされることなく) 作業を終了します。
ここから重要な詳細がわかります。デーモン スレッドはいつでも終了できるということです。その結果、データの整合性については保証がありません。したがって、デーモン スレッドは特定のハウスキーピング タスクに適しています。たとえば、Java にはメソッド呼び出しの処理を担当するスレッドfinalize()、つまりガベージ コレクター (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);
}
ゼロによる除算はエラーを引き起こし、ハンドラーによって捕捉されます。独自のハンドラーを指定しない場合、JVM はデフォルトのハンドラーを呼び出し、例外のスタック トレースを StdError に出力します。各スレッドには優先順位もあります。優先順位の詳細については、「マルチスレッドにおける 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()メソッド内で開始されます。これらのメソッドを混同しないでください。r メソッドを直接呼び出すとun()、新しいスレッドは開始されません。これは、start()JVM に新しいスレッドの作成を要求するメソッドです。Thread を継承するこのオプションは、クラス階層に Thread を含めているという点ですでに問題があります。2 番目の欠点は、「単一責任」の原則に違反し始めていることです。つまり、このクラスは、スレッドの制御と、このスレッドで実行されるタスクの制御を同時に担当します。正しい方法は何ですか? 答えは同じrun()メソッドで見つかり、これをオーバーライドします。

public void run() {
	if (target != null) {
		target.run();
	}
}
ここにあるのは、 Thread クラスのインスタンスを作成するときに渡すことができるtargetsome です。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();
    }
}
RunnableJava 1.8 以降は機能インターフェイスでもあります。これにより、スレッドのタスクに対してさらに美しいコードを書くことが可能になります。

public static void main(String[] args) {
	Runnable task = () -> { 
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

結論

この議論で、スレッドとは何か、スレッドがどのように存在するのか、スレッドを使用してどのような基本操作を実行できるのかが明確になることを願っています。次のパートでは、スレッドがどのように相互作用するかを理解し、スレッドのライフサイクルを探っていきます。 Java と Thread クラスを組み合わせるとさらに効果的です。パート II — 同期 併用するとより効果的です: Java と Thread クラス。パート III — 連携 を強化: Java と Thread クラス。パート IV — Callable、Future、およびその仲間たち 一緒にさらに良く: Java と Thread クラス。パート V — Executor、ThreadPool、Fork/Join を 組み合わせるとさらに効果的: Java と Thread クラス。パート VI — 撃て!
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION