記事ディレクトリ
マルチスレッド
マルチスレッドの概要
スレッドとは?
スレッドは、プログラム内の実行パスです。
前にプログラムの実行を開始した後、main メソッドの実行は実際には別の実行パスです。
public static void main(String[] args) { // 代码... for (int i = 0; i < 10; i++) { System.out.println(i); } // 代码... }
プログラムに実行パスが 1 つしかない場合、そのプログラムはシングルスレッド プログラムです。
マルチスレッドとは?
マルチスレッドとは、ハードウェアとソフトウェアから複数の実行プロセスを実装する技術を指します。
マルチスレッド作成
方法 1: Thread クラスを継承する
スレッドクラス:
Java は、java.lang.Thread クラスを通じてスレッドを表します。
オブジェクト指向の考え方によれば、Thread クラスはマルチスレッドを実現する方法を提供する必要があります。
Thread クラスはスレッド メソッドを作成します。
- サブクラス MyThread を定義して、スレッド クラス java.lang.Thread を継承します。
- サブクラス MyThread の run() メソッドを書き直します
- MyThread クラスのインスタンス オブジェクトを作成する
- 子スレッド オブジェクトの start() メソッドを呼び出してスレッドを開始します (run メソッドは起動後も実行されます)。
Thread クラスは、次のようにマルチスレッドのサンプル コードを作成します。
public class ThreadDemo {
public static void main(String[] args) {
// 3. 创建MyThread类的实例对象
Thread t = new MyThread();
// 4. 调用子线程对象的start方法启动线程(启动后还是执行的run方法)
t.start();
// 主线程中执行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出: " + i);
}
}
}
/**
1. 定义一个线程类继承自Thread
*/
class MyThread extends Thread {
/**
2. 重写run方法, run方法中定义创建出来的线程要做什么
*/
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行输出: " + i);
}
}
}
Thread クラスには、複数のスレッドを作成するという欠点があります。
長所:コーディングが簡単
短所: スレッド クラスは Thread を継承しており、他のクラスを継承できないため、展開に有利ではありません; スレッドが実行結果を持っている場合、それを直接返すことはできません。このメソッドは、機能の実行にのみ適しています。
前述のように、start メソッドを呼び出した後でも run メソッドが実行されるため、run メソッドを直接実行してみませんか?
run メソッドを直接呼び出すと、通常のメソッドとして実行されます。これは、現時点ではシングルスレッド実行と同等です。
start メソッドを呼び出すだけで、実行のために新しいスレッドが開始されます。
注: タスクを実行するメイン スレッドのコードをサブスレッドの前に配置しないでください。これは、サブスレッドは常にメイン スレッドの実行が終了した後に開始されるためです。これは、シングル スレッドの効果と同等です。
方法 2: Runnable インターフェイスを実装する
Runnable インターフェイスを実装してマルチスレッドを作成する方法は次のとおりです。
- Runnable インターフェイスを実装するスレッド タスク クラス MyRunnable を定義します。
- タスク クラス MyRunnable の run() メソッドをオーバーライドする
- MyRunnable タスク クラス オブジェクトを作成する
- MyRunnable タスク オブジェクトを Thread に渡して処理します。
- スレッド オブジェクトの start() メソッドを呼び出して、スレッドを開始します。
Runnable インターフェイスを実装して、マルチスレッドのサンプル コードを作成します。
public class ThreadDemo2 {
public static void main(String[] args) {
// 3. 创建MyRunnable 任务对象
Runnable target = new MyRunnable();
// 4. 把任务对象交给线程对象(Thread)处理
Thread t = new Thread(target);
// 5. 启动线程
t.start();
// 主线程要执行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出: " + i);
}
}
}
/**
1. 定义一个线程任务类, 实现Runnable接口
*/
class MyRunnable implements Runnable {
/**
2. 任务类MyRunnable中重写run方法
*/
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行输出: " + i);
}
}
}
Runnable インターフェースを実装してマルチスレッドの匿名内部クラスを作成することは、単純化された方法 1 を意味します。
public static void main(String[] args) {
// 1. 创建任务对象(匿名内部类的方式)
Runnable target = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行输出: " + i);
}
}
};
// 2. 将任务对象交给线程对象(Thread)处理
Thread t = new Thread(target);
// 3. 启动线程
t.start();
// 主线程执行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出: " + i);
}
}
Runnable インターフェースを実装してマルチスレッドの匿名内部クラスを作成することは、2 番目の方法を単純化することを意味します。
public static void main(String[] args) {
// 1. 创建任务对象(匿名内部类的方式)
// 2. 将任务对象交给线程对象(Thread)处理
// 3. 启动线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行输出: " + i);
}
}
}).start();
// 主线程执行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出: " + i);
}
}
Runnable インターフェイスを実装して、マルチスレッドの匿名内部クラスを Lambda 式と組み合わせて作成し、3 番目の方法を簡素化します。
public static void main(String[] args) {
// 1. 创建任务对象(匿名内部类的方式)
// 2. 将任务对象交给线程对象(Thread)处理
// 3. 启动线程
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行输出: " + i);
}
}).start();
// 主线程执行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出: " + i);
}
}
Runnable インターフェースを実装して複数のスレッドを作成する利点と欠点:
利点: スレッド タスク クラスはインターフェイスのみを実装し、クラスを継承してインターフェイスを実装し続けることができ、強力なスケーラビリティを備えています。
短所: プログラミング用のオブジェクト パッケージの余分なレイヤーがある. スレッドが実行結果を持っている場合、返された結果を取得できません。
方法 3: Callable インターフェイスを実装する
最初の 2 つのスレッド作成方法には問題があります。
書き換えられた run メソッドは、結果を直接返すことはできません。
スレッドの実行結果を返す必要があるビジネス シナリオには適していません。
では、この問題を解決するにはどうすればよいでしょうか。
JDK 5.0 は、Callable と FutureTask の組み合わせを提供して、マルチスレッドを実現します。
このようにマルチスレッドを実装する利点は、スレッド実行の結果を取得できることです。
マルチスレッド実装スキーム 3: Callable および FutureTask インターフェイスを使用して実装します。実装手順は次のとおりです。
①タスクオブジェクトを取得する
- Callable インターフェースを実装するクラスを定義し、call メソッドを書き直します。call メソッドは、やりたいことをカプセル化し、call メソッドは結果を返すことができます。
- Callable タスク オブジェクトを FutureTask に渡して、それをスレッド タスク オブジェクトにカプセル化します。
②スレッドタスクオブジェクトをスレッドオブジェクト(Thread)に渡して処理する。
③Threadのstartメソッドを呼び出してスレッドを起動し、タスクを実行する
④ スレッド実行後、FutureTask の get メソッドでタスク実行結果を取得する。
FutureTask パッケージを使用する役割:
機能 1: スレッド スレッド オブジェクト コンストラクターは Runnable 型のオブジェクトのみを受け入れ、FutureTask は Runnable インターフェースを実装します。これは、処理のために Thread に渡すことができます。
機能 2: スレッドの実行が完了した後に get メソッドを呼び出すことで、スレッドの実行結果を取得できます。
FutureTask API
メソッド名 | 例証する |
---|---|
FutureTask<>(呼び出し可能な呼び出し) | Callable オブジェクトを FutureTask オブジェクトにカプセル化します。 |
get() は例外をスローします | スレッド実行呼び出しメソッドによって返された結果を取得します。 |
デモコード
public class ThreadDemo4 {
public static void main(String[] args) {
// 3. 创建任务对象
Callable<String> callable = new MyCallable(5);
// 4. 将任务对象包装成线程任务对象
FutureTask<String> ft = new FutureTask<>(callable);
// 5. 将线程任务对象交给线程对象(Thread)处理
Thread t = new Thread(ft);
// 6. 启动线程
t.start();
// 获取线程执行的结果, 会返回正常的结果和异常的结果
try {
String res = ft.get();
System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
1. 定义类实现Callable接口
泛型自定义返回值类型
*/
class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
/**
2. 重新实现类call方法(任务方法)
*/
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <= n ; i++) {
sum += i;
}
return "子线程方法执行的结果是" + sum;
}
}
方法 3 の長所と短所:
利点: スレッド タスク クラスはインターフェイスのみを実装し、クラスを継承してインターフェイスを実装し続けることができ、強力なスケーラビリティを備えています。
スレッドの実行結果は、スレッドの実行が完了した後に取得できます。
短所: コーディングが少し複雑になります。
要約: 3 つの方法の比較は次のとおりです。
道 | アドバンテージ | 欠点 |
---|---|---|
Thread クラスを継承する | プログラミングは比較的簡単で、Thread クラスのメソッドを直接使用できます。 | スケーラビリティが低い、他のクラスを継承できなくなった、スレッド実行の結果を返せない |
Runnable インターフェースを実装する | 拡張性が高く、このインターフェイスを実装しながら他のクラスを継承できます。 | プログラミングが比較的複雑で、スレッド実行の結果を返せない |
Callable インターフェースを実装する | 拡張性が高く、このインターフェイスを実装しながら他のクラスを継承できます。スレッドの実行結果が得られる | プログラミングは比較的複雑 |