スレッドはオペレーティング システム操作の基本単位であり、プロセス内にカプセル化されます。スレッドを手動で作成しなくても、プロセスではデフォルトのスレッドが実行されます。
JVM の場合、実行するシングルスレッド プログラムを作成すると、JVM 内で少なくとも 2 つのスレッドが実行されます。1 つは作成したプログラムで、もう 1 つはガベージ コレクションです。
スレッドの基本情報
Thread.currentThread() メソッドを通じて現在のスレッドに関する情報を取得し、それを変更できます。
次のコードを見てみましょう。
Thread.currentThread().setName("テスト");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
名前 = Thread.currentThread().getName();
優先度 = Thread.currentThread().getPriority();
groupName = Thread.currentThread().getThreadGroup().getName();
isDaemon = Thread.currentThread().isDaemon();
System.out.println("スレッド名:" + 名前);
System.out.println("優先度:" + 優先度);
GroupName では、各スレッドはデフォルトでスレッド グループに属します。また、スレッド グループにサブスレッド グループを含めることもできます。これにより、スレッドとスレッド グループがツリー構造を形成します。
Name 、各スレッドには名前が付けられます。明示的に指定しない場合、名前のルールは「Thread-xxx」です。
Priority 、各スレッドは独自の優先順位を持ち、JVM の優先順位の処理方法は「プリエンプティブ」です。 JVM は優先度の高いスレッドを見つけると、すぐにそのスレッドを実行し、同じ優先度を持つ複数のスレッドに対してポーリングを行います。 Java のスレッド優先順位の範囲は 1 ~ 10 で、デフォルトは 5 です。Thread クラスは、最高と最低の優先順位を表す MIN_PRIORITY と MAX_PRIORITY という 2 つの定数を定義します。
次のコードを見ると、異なる優先順位を持つ 2 つのスレッドが定義されています。
スレッド thread2 = 新しいスレッド("高")
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("スレッド 2 が実行中です。");
}
}
};
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
thread1.start();
}
スレッドの作成方法
上記のコンテンツはすべて、デフォルトのスレッドの情報を示しています。では、スレッドを作成するにはどうすればよいでしょうか? Java では、スレッドを作成する方法が 3 つあります。
Java のスレッドは Thread クラスを継承するか、Runnable インターフェイスを実装します。1 つずつ見ていきましょう。
内部クラスを使用してスレッドを作成する
内部クラスを使用してスレッドを作成できます。そのプロセスでは、Thread 型の変数を宣言し、run メソッドをオーバーライドします。サンプルコードは次のとおりです。
上記と同様の方法で、Thread からクラスを派生し、その run メソッドをオーバーライドできます。サンプルコードは次のとおりです。
public static void createThreadBySubClass()
{
MyThread スレッド = new MyThread();
thread.start();
}
Runnable インターフェイスを実装するクラスを定義し、このクラスのインスタンスをパラメータとして使用して Thread 変数コンストラクターを構築できます。サンプルコードは次のとおりです。
public static void createThreadByRunnable()
{
MyRunnable runnable = new MyRunnable();
スレッド thread = 新しいスレッド (実行可能);
thread.start();
}
これには、Java でのマルチスレッドの実行モードが関係します。Java の場合、マルチスレッドが実行されている場合、「マルチオブジェクト マルチスレッド」と「単一オブジェクト マルチ スレッド」には違いがあります。
マルチオブジェクト マルチスレッド では、プログラムは実行中に複数のスレッド オブジェクトを作成し、各オブジェクトで 1 つのスレッドが実行されます。
単一オブジェクト マルチスレッド、プログラムは実行中にスレッド オブジェクトを作成し、その上で複数のスレッドを実行します。
明らかに、スレッドの同期とスケジューリングの観点からは、マルチオブジェクト マルチスレッドの方が簡単です。上記 3 つのスレッド作成方法のうち、最初の 2 つは「マルチオブジェクト マルチスレッド」で、3 つ目は「マルチオブジェクト マルチスレッド」または「単一オブジェクト シングル スレッド」のいずれかを使用できます。
次のサンプル コードを見てみましょう。このコードでは、Object.notify メソッドがオブジェクトのスレッドを起動し、Object.notifyAll メソッドがオブジェクトのすべてのスレッドを起動します。
public static void main(String[] args) は InterruptedException をスローします
{
通知テスト();
通知テスト2();
通知テスト3();
}
private static void NoticeTest() が InterruptedException をスローする
{
MyThread[] arrThreads = 新しい MyThread[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = 新しい MyThread();
arrThreads[i].id = i;
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrThreads.length; i++)
{
同期化(arrThreads[i])
{
arrThreads[i].notify();
}
}
}
private static void NoticeTest2() が InterruptedException をスローする
{
MyRunner[] arrMyRunners = 新しい MyRunner[3];
スレッド[] arrThreads = 新しいスレッド[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrMyRunners[i] = 新しい MyRunner();
arrMyRunners[i].id = i;
arrThreads[i] = 新しいスレッド(arrMyRunners[i]);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrMyRunners.length; i++)
{
同期(arrMyRunners[i])
{
arrMyRunners[i].notify();
}
}
}
private static void NoticeTest3() が InterruptedException をスローする
{
MyRunner ランナー = new MyRunner();
スレッド[] arrThreads = 新しいスレッド[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = 新しいスレッド(ランナー);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
同期(ランナー)
{
ランナー.notifyAll();
}
}
}
クラス MyThread は Thread を拡張します
{
パブリック int ID = 0;
public void run()
{
System.out.println("スレッド " + id + " は 5 分間スリープする準備ができています。");
試す
{
同期済み(これ)
{
this.wait(5*60*1000);
}
}
catch(中断例外例)
{
ex.printStackTrace();
}
System.out.println("「番目」 + id + "スレッドが起動されました。");
}
}
MyRunner クラスは Runnable を実装します
{
パブリック int ID = 0;
public void run()
{
System.out.println("スレッド " + id + " は 5 分間スリープする準備ができています。");
試す
{
同期済み(これ)
{
this.wait(5*60*1000);
}
}
catch(中断例外例)
{
ex.printStackTrace();
}
System.out.println("「番目」 + id + "スレッドが起動されました。");
}
}
NoticeAll メソッドは、オブジェクト上の 1 つのスレッドのみをランダムに起動するため、「単一オブジェクトのマルチスレッド」シナリオに適しています。
スレッド状態の切り替え
スレッドの場合、作成時からスレッドが終了するまで、このプロセス中のスレッドのステータスは次のようになります。
作成: Thread インスタンスはすでに存在しますが、CPU にはまだリソースとタイム スライスが割り当てられています。
Ready: スレッドは実行に必要なすべてのリソースを取得しており、CPU が時間をスケジュールするのを待っているだけです。
実行中: スレッドは現在の CPU タイム スライス内にあり、関連するロジックを実行しています。
スリープ: 通常、Thread.sleep を呼び出した後の状態。この時点では、スレッドは動作に必要なさまざまなリソースをまだ保持していますが、CPU によってスケジュールされません。
サスペンド: 通常、Thread.suspend を呼び出した後の状態。スリープと同様に、CPU はスレッドをスケジュールしません。この状態では、スレッドがすべてのリソースを解放します。
死亡: スレッドが終了するか、Thread.stop メソッドが呼び出されます。
次に、スレッドの状態を切り替える方法を説明します。まず、次のメソッドを使用します。
Thread() または Thread(Runnable): スレッドを構築します。
Thread.start: スレッドを開始します。
Thread.sleep: スレッドをスリープ状態に切り替えます。
Thread.interrupt: スレッドの実行を中断します。
Thread.join: スレッドが終了するまで待ちます。
Thread.yield: CPU 上のスレッドの実行タイム スライスを剥奪し、次のスケジューリングを待ちます。
Object.wait: オブジェクト上のすべてのスレッドをロックし、通知メソッドが発行されるまで実行を継続しません。
Object.notify: オブジェクト上のスレッドをランダムに起動します。
Object.notifyAll: Object 上のすべてのスレッドを起動します。
さあ、デモンストレーションの時間です! ! !
スレッドが待機して目覚めています
ここでは主に Object.wait メソッドと Object.notify メソッドが使用されます。上記の通知インスタンスを参照してください。 runnable インターフェイスを実装してスレッドを作成する場合、wait と notification はどちらも同じオブジェクトをターゲットにする必要があることに注意してください。Thread オブジェクトではなく、Runnable オブジェクトでこれら 2 つのメソッドを使用する必要があります。
スレッドの睡眠と覚醒
public static void main(String[] args) は InterruptedException をスローします
{
sleepTest();
}
private static void sleepTest() が InterruptedException をスローする
{
スレッド thread = new Thread()
{
public void run()
{
System.out.println("Thread" + Thread.currentThread().getName() + "5 分間スリープします。");
試す
{
Thread.sleep(5*60*1000);
}
catch(中断例外例)
{
System.out.println("スレッド" + Thread.currentThread().getName() + "スリープが中断されました。");
}
System.out.println("スレッド" + Thread.currentThread().getName() + "スリープが終了しました。");
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
Thread.stop メソッドもありますが、このメソッドは推奨されません。上記のスリープおよびウェイクアップのメカニズムを使用して、IterruptedException の処理時にスレッドを終了させることができます。
public static void main(String[] args) は InterruptedException をスローします
{
stopTest();
}
private static void stopTest() が InterruptedException をスローする
{
スレッド thread = new Thread()
{
public void run()
{
System.out.println("スレッドは実行中です。");
試す
{
Thread.sleep(1*60*1000);
}
catch(中断例外例)
{
System.out.println("スレッド割り込み、スレッド終了");
戻る;
}
System.out.println("スレッドは正常に終了しました。");
}
};
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
メインスレッドに 10 個のサブスレッドを作成し、10 個のサブスレッドがすべて完了すると、メインスレッドが次のロジックを実行すると予想されます。この時点で、Thread.join が表示されます。
public static void main(String[] args) は InterruptedException をスローします
{
joinTest();
}
private static void joinTest() が InterruptedException をスローする
{
スレッド thread = new Thread()
{
public void run()
{
試す
{
for(int i = 0; i < 5; i++)
{
System.out.println("スレッドは実行中です。");
Thread.sleep(1000);
}
}
catch(中断例外例)
{
ex.printStackTrace();
}
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(1000);
thread.join();
System.out.println("メインスレッドが正常に終了しました。");
}
}
プロセスの下にあるすべてのスレッドがメモリ空間を共有していることはわかっていますが、異なるスレッド間でメッセージを転送するにはどうすればよいでしょうか? Java I/O をレビューするときに、PipedStream と PipedReader について話しましたが、ここでそれらが登場します。
次の 2 つの例はまったく同じ機能を持っています。違いは、1 つは Stream を使用し、もう 1 つは Reader/Writer を使用することです。
スレッド thread1 = 新しい Thread()
{
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
試す
{
その間(真)
{
文字列メッセージ = br.readLine();
pos.write(message.getBytes());
if (message.equals("end")) ブレーク;
}
br.close();
pos.close();
}
catch(例外例)
{
ex.printStackTrace();
}
}
};
スレッド thread2 = 新しい Thread()
{
public void run()
{
byte[] バッファ = 新しいバイト [1024];
int bytesRead = 0;
試す
{
while((bytesRead = pis.read(buffer, 0,buffer.length)) != -1)
{
System.out.println(new String(buffer));
if (new String(buffer).equals("end")) ブレーク;
バッファ = null;
バッファ = 新しいバイト[1024];
}
pis.close();
バッファ = null;
}
catch(例外例)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
スレッド thread1 = 新しい Thread()
{
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
試す
{
その間(真)
{
文字列メッセージ = br.readLine();
bw.write(メッセージ);
bw.newLine();
bw.flush();
if (message.equals("end")) ブレーク;
}
br.close();
pw.close();
bw.close();
}
catch(例外例)
{
ex.printStackTrace();
}
}
};
スレッド thread2 = 新しい Thread()
{
public void run()
{
文字列行 = null;
試す
{
while((line = br.readLine()) != null)
{
System.out.println(line);
if (line.equals("end")) ブレーク;
}
br.close();
pr.close();
}
catch(例外例)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}