Java マルチスレッドは Java 言語の非常に重要な機能であり、プログラムが複数のタスクを同時に実行できるようになります。マルチスレッド化により、プログラムは複数のタスクを同時に処理できるため、プログラムの実行時間が短縮されます。さらに、マルチスレッドは、マルチコア プロセッサを活用して、コンピュータ ハードウェアのパフォーマンスをより有効に活用するのにも役立ちます。
では、Java でマルチスレッドを実装するにはどうすればよいでしょうか?
今日は、最も単純なThread
ものからRunnable
、今日よく使用されるものまで説明します。CompletableFuture
糸
Java では、Thread
クラスのインスタンスを作成することによってスレッドが作成されます。スレッドを作成する最も簡単な方法は、Thread
クラスを拡張してrun()
メソッドをオーバーライドすることです。run()
メソッド内に実行するコードを記述し、プログラム内でメソッドを呼び出してstart()
スレッドを開始します。例えば:
public class MyThread extends Thread {
public void run() {
// 线程要执行的代码
for (int i = 0; i < 10; i++) {
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + i + "}");
}
}
}
public static void main(String[] args) {
System.out.println("start");
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
System.out.println("end");
}
もちろん、面倒であれば匿名内部クラスを直接使っても構いません。
// 效果是一样的
System.out.println("start");
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "{" + i + "}");
}
}).start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "{" + i + "}");
}
}).start();
System.out.println("end");
ここで、スレッドを同期させるメソッドは使用しないでくださいrun()
。同時に、マルチスレッドコンテンツの実行後に後続のコードを実行したい場合は、このメソッドを使用してスレッドを同期させることができますjoin()
。実行を終了したら、以下の操作を行ってください。
もちろん、sleep()
メインスレッドを一定時間スリープさせることもできますが、このスリープ時間は主観的なものであり、独自に決定するため、この方法はお勧めできません。
実行可能
さらに、Runnable
インターフェイスを使用してスレッドを作成することもできます。Runnable
インターフェイスにrun()
はメソッドが 1 つだけあり、実行するコードを記述し、Runnable
オブジェクトをパラメータとしてThread
クラスのコンストラクターに渡し、start()
そのメソッドを呼び出してスレッドを開始します。例えば:
public class MyThread implements Runnable {
@Override
public void run() {
// 线程要执行的代码
for (int i = 0; i < 10; i++) {
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + i + "}");
}
}
}
実はThread
ほぼ同じです 実装インターフェースと継承クラスだけです 直接使う場合は両者に大きな違いはありません ただし、リソース共有が実現しやすいと
言うところもありますが、しかし、テストした結果、リソース共有も実現できると思います。Runnable
Thread
Thread
テストコード
public class MyThread extends Thread {
private int ticket = 5;
@Override
public void run() {
// 双检索机制——保证线程的安全
if (ticket > 0) {
synchronized (this) {
if (ticket > 0) {
while (true) {
System.out.println("Thread:" + Thread.currentThread().getName() + "--Thread ticket = " + ticket--);
if (ticket < 0) {
break;
}
}
}
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
new Thread(myThread1).start();
new Thread(myThread1).start();
new Thread(myThread1).start();
new Thread(myThread1).start();
new Thread(myThread1).start();
new Thread(myThread1).start();
}
}
执行结果如下:
Thread:Thread-1--Thread ticket = 5
Thread:Thread-1--Thread ticket = 4
Thread:Thread-1--Thread ticket = 3
Thread:Thread-1--Thread ticket = 2
Thread:Thread-1--Thread ticket = 1
Thread:Thread-1--Thread ticket = 0
ソース コードを調べたところThread
、実際にはインターフェイスThread
を実装しているだけで、さらに多くのメソッドが提供されていることがわかりました。Runnable
したがって、違いはありませThread
んRunnable
。違いがあるとすれば、それはクラスとインターフェースの違い、継承と実装の違いです。
呼び出し可能 (Future 付き)
子スレッドの場合、次の 2 つのニーズが発生することがあります。
- サブスレッドの実行結果を取得する
- 子スレッドの実行ステータス(成功、失敗、例外)を取得します。
Thread
Runnable
これら 2 つの要件をどちらも満たさない場合、ステータスRunnable
は取得できますが、結果は取得できないため、このように表示されましたCallable
。サブスレッドの実行結果を取得するためにCallable
一緒に使用されます。( Javaマルチスレッドにおける非同期計算方式であり、タスク実行中に非同期計算結果を取得するために使用できます)Future
Future
public class MyThread implements Callable<Integer> {
private Integer number;
public Integer getNumber(){
return number;}
public MyThread (Integer number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int result = 0;
for (int i = 0; i < number; i++) {
result ++;
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
}
return result;
}
}
public static void main(String[] args) throws Exception {
System.out.println("start");
MyThread myThread1= new MyThread (10);
MyThread myThread2= new MyThread (15);
//使用Executors工厂类创建一个简单的线程池(线程数:2)
ExecutorService executor = Executors.newFixedThreadPool(2);
// 将任务提交给线程池
Future<Integer> submit1 = executor.submit(myThread1);
Future<Integer> submit2 = executor.submit(myThread2);
// 获取任务的执行结果
System.err.println(submit1.get());
System.err.println(submit2.get());
executor.shutdown();
System.out.println("end");
}
この例では、インターフェイスを実装し、メソッドをオーバーライドするMyThread
クラスを実装します。このメソッドでは、長時間実行されるタスクをシミュレートし、計算結果を実行結果として返します。main メソッドでは、まず固定数のスレッドでスレッド プールを作成し、次にインスタンスをメソッドに渡してタスクを送信し、タイプのオブジェクトを取得します。このオブジェクトのメソッドを呼び出すことで、タスクの実行結果を取得できます。最後に、スレッド プールをシャットダウンします。Callable
call()
call()
ExecutorService
MyThread
submit
Future
get()
Future
呼び出されたメソッドがget()
ブロッキングしているため、それに対処する必要がある場合があることに注意してくださいtry-catch
。また、タスクの完了後にリソースを解放するには、スレッド プールを閉じる必要があります。
これは単純な使用例ですが、Callable
複数のオブジェクトを組み合わせることにより、Future
より複雑な同時計算を実現できます。
未来
もちろんFuture
単体でも使えます。
使用時は、タスクの実行待ちをブロックして計算結果を取得するメソッドを呼び出したり、Future
タスクが完了したかどうかを判定するメソッドを呼び出したり、タスク実行中に例外が発生した場合に、メソッドを呼び出して例外情報を取得するget()
isDone()
get()
public static void main(String[] args) throws Exception {
System.out.println("start");
ExecutorService executor = Executors.newFixedThreadPool(2);
Future future1 = executor.submit(() -> {
int result = 0;
for (int i = 0; i < number; i++) {
result ++;
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
}
return result;
});
Future future2 = executor.submit(() -> {
int result = 0;
for (int i = 0; i < number; i++) {
result ++;
System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
}
return result;
});
String result1 = (String) future1.get();
String result2 = (String) future2.get();
System.out.println(result1);
System.out.println(result2);
executor.shutdown();
System.out.println("end");
}
call()
実際には、メソッド内のビジネス ロジックがメイン コードに冗長であり、結合度が高いことを除いて、前のメソッドと同じです。いくつかの単純なコードしか使用できない場合、複雑な場合は、このメソッドをお勧めします。比較のためにそれらを一緒に使用しますcallable
。
完成可能な未来
CompletableFuture
は Java 8 で導入された非常に便利な機能で、特に非同期操作に複数のステージが含まれる場合に、非同期操作をより簡単に処理できるようになります。
CompletableFuture
インターフェースを継承しFuture
、計算が完了した後に計算結果を取得できるため、 のFuture
特徴もあります。さらに、他のいくつかの機能も提供します。
- 非同期実行機能とシリアル実行機能
- オブザーバー メカニズムは、タスクの実行後にトリガーできます。
- 2 つのタスクを組み合わせて実行することで、より複雑な非同期シナリオを処理できます。
現在、開発ではより複雑なロジックを使用することが一般的になっています。CompletableFuture
作成する
CompletableFuture
runAsync()
メソッドを使用して直接非同期に実行できます
CompletableFuture.runAsync(() -> {
// 在这里执行一些耗时的操作
});
非同期処理
CompletableFuture
の結果
CompletableFuture
静的メソッドを使用してsupplyAsync()
非同期実行を作成しCompletableFuture
、lambda
その計算ジェネレーターとして式を提供できます。
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
// 在这里执行一些耗时的操作
return "some result";
});
completableFuture.thenAccept(result -> {
System.out.println("Got result: " + result);
});
// 这里可以继续执行其他任务,completableFuture将在后台继续执行并在完成后触发回调函数。
複数操作する
CompletableFuture
特定のタスクを実行する前に複数のタスクが完了するのを待つ必要がある場合は、静的メソッドまたは以下をCompletableFuture
使用できます。CompletableFuture
allOf()
anyOf()
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
// 在这里执行一些耗时的操作
return "Result of future 1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
// 在这里执行一些耗时的操作
return "Result of future 2";
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
allFutures.thenRun(() -> {
//这边join和get方法都可以 只是抛出异常的区别
String result1 = future1.join();
String result2 = future2.join();
System.out.println("Got both results: " + result1 + " and " + result2);
});
CompletableFuture
スレッド プールはデフォルトで使用されますForkJoinPool
。スレッド プールを自分で定義したい場合は、Executors
直接作成できます。ただし、Executors
これはスレッド プール自体ではなく、スレッド プールを作成するための静的メソッドを提供するファクトリ クラスであるため、スレッドコアスレッド数、最大スレッド数、拒否ポリシーなどの詳細パラメータは設定できません。これらのパラメータを設定する必要がある場合は、ThreadPoolExecutor
クラスを使用してスレッド プールを手動で作成する必要があります。
/**
* public ThreadPoolExecutor(int corePoolSize,
* int maximumPoolSize,
* long keepAliveTime,
* TimeUnit unit,
* BlockingQueue<Runnable> workQueue,
* ThreadFactory threadFactory,
* RejectedExecutionHandler handler) {}
* 1. corePoolSize:核心线程数;当提交的任务数大于 corePoolSize 时,线程池会自动扩容。
*
* 2. maximumPoolSize:线程池最大线程数;当活动线程数达到该值,并且 workQueue 队列已满,则执行拒绝策略。
*
* 3. keepAliveTime:线程空闲超时时间,超过该时间则进行回收。
*
* 4. unit:keepAliveTime 的时间单位。
*
* 5. workQueue:任务阻塞队列,用于存储提交但尚未执行的任务。
*
* 6. threadFactory:线程工厂,用于创建线程。
*
* 7. handler:拒绝策略,当线程数量已经达到 maximumPoolSize 并且队列已满时,采取的策略。
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
10,
10,
TimeUnit.SECONDS,
new LinkedBlockingQueue(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
これ
springboot
も非常に頻繁に使用されるメソッドがあります。@async
このアノテーションをメソッドに追加し、スタートアップ クラスに追加するだけで、@Enableasync
マルチスレッド例外操作を直接実装できます。ただし、このマルチスレッド メソッドは、依然として違いは、@async
メソッド呼び出し時にマルチスレッドの非同期操作が実行されますが、メソッド内のビジネス ロジックは同期しているため、通常どおり併用できます (もちろん、ビジネス ロジックを抽出することもできますComplatableFuture
)真ん中に@async
「はははは」を追加します))
Java のマルチスレッド機構は Java プログラミングに不可欠な要素であり、Java のマルチスレッド プログラミング機能を理解し、使い慣れることで、プログラマーの作業効率とプログラミング レベルが大幅に向上します。