マルチスレッド
java.Thread
—スレッド概要
-マルチタスク:
- 食事をしながらスマホを見る
- ワイルドなドライバー:運転しながら電話をかけ、点滴を打つ
- トイレでスマホを使う
現実ではこのように同時に複数のタスクを行う例がたくさんあります。一見すると複数のタスクが同時に進んでいるように見えますが、本質的には私たちの脳は同じ時間に一つのことしか行っていません。
-プロセスProcess:
-プログラム:プログラムは命令とデータの集合体であり、それ自体には実行という意味はありません。静的な概念です。
-プロセス:プロセスはプログラムの実行プロセスであり、動的な概念です。システムリソースの単位として割り当てられます。
-通常、一つのプロセスには複数のスレッドが含まれます。もちろん、一つのプロセスには少なくとも一つのスレッド(メインスレッド)が存在します(さもなければプロセスの存在意義がありません)。スレッドはCPUのスケジューリングと実行の単位です。
-スレッドThread:
-独立して実行されるパス。
-プログラム実行時、自分でスレッドを作成しなくても、メインスレッド(ユーザースレッド)やガベージコレクション(GC)スレッド(デーモンスレッド)などの複数のスレッドがバックグラウンドで動作します。
-多くのマルチスレッドはシミュレーションされたものであり、真のマルチスレッドとは複数のCPU、つまりマルチコアがある場合(サーバーなど)を指します。一つのCPUの場合、シミュレートされたマルチスレッドとなり、同じ時間点でCPUは一つのコードしか実行できません。切り替えが非常に速いため、同時に実行されているかのような錯覚が生まれます(人脳が複数のタスクを行う場合と同様)。
-マルチスレッド:
-元々一本の道路があり、車が多くなるにつれて道路が渋滞し、効率が極端に低下しました。
効率を向上させ、道路を最大限に活用するため、複数の車線が追加されました。
-ビデオを見ているとき、画面、音声、コメントなどを同時に見ることができます。これは一つのプロセス内に多くのスレッドが存在することを示しています。
-一つのプロセス内で複数のスレッドが確立され、スレッドの実行はスケジューラによって管理されます。スケジューラはOSと密接に関連しており、その順序は人為的に介入することはできません。
-同じリソースを操作する場合、リソースの競合問題が発生する可能性があり、同時実行制御を追加する必要があります。
-スレッドは追加のオーバーヘッドをもたらします。たとえば、CPUスケジューリング時間や同時実行制御のオーバーヘッドです。
-各スレッドは独自の作業メモリで相互作用し、メモリ管理が不適切な場合はデータの不整合が発生する可能性があります。
—スレッドの実装
スレッドの作成:
-3つの作成方式:Thread、Runnable、Callable。
-Threadクラスの継承
(重要)
- カスタムスレッドクラスがThreadクラスを継承します。
- run()メソッドをオーバーライドし、スレッド実行本体を記述します。
- スレッドオブジェクトを作成し、start()メソッドを呼び出してスレッドを起動します。
package jp.example.threads;
//スレッド作成方法の一つ:Threadクラスを継承する
//手順:Threadクラスを継承し、run()メソッドをオーバーライドし、start()メソッドを呼び出します。
public class ThreadDemo extends Thread {
//run()メソッドをオーバーライド
@Override
public void run() {
//runメソッドのスレッド本体
for (int i = 0; i < 20; i++) {
System.out.println("コードを確認中 " + i);
}
}
public static void main(String[] args) {
//スレッドオブジェクトを作成
ThreadDemo demoThread = new ThreadDemo();
//start()メソッドを呼び出してスレッドを開始
demoThread.start();//スレッドはすぐに実行されるとは限りません。CPUがスケジューリングします。
//メインスレッド
for (int i = 0; i < 20; i++) {
System.out.println("メインスレッドを学習中 " + i);
}
}
}
結果:
メインスレッドを学習中 0
メインスレッドを学習中 1
メインスレッドを学習中 2
メインスレッドを学習中 3
メインスレッドを学習中 4
メインスレッドを学習中 5
メインスレッドを学習中 6
メインスレッドを学習中 7
メインスレッドを学習中 8
メインスレッドを学習中 9
メインスレッドを学習中 10
メインスレッドを学習中 11
メインスレッドを学習中 12
コードを確認中 0
コードを確認中 1
コードを確認中 2
コードを確認中 3
コードを確認中 4
コードを確認中 5
コードを確認中 6
メインスレッドを学習中 13
メインスレッドを学習中 14
メインスレッドを学習中 15
コードを確認中 7
メインスレッドを学習中 16
メインスレッドを学習中 17
コードを確認中 8
コードを確認中 9
コードを確認中 10
コードを確認中 11
コードを確認中 12
コードを確認中 13
コードを確認中 14
コードを確認中 15
コードを確認中 16
メインスレッドを学習中 18
コードを確認中 17
コードを確認中 18
コードを確認中 19
メインスレッドを学習中 19//交互に実行される
- run()メソッドとstart()メソッドの呼び出しの違い:
-run()メソッドを呼び出すことは、プロセス内に直接プログラムを追加することに相当します。
-start()メソッドを呼び出すことは、サブスレッドを開拓し、メインスレッドとCPUリソースを競合させることに相当します。
-Runnableインターフェース
(非常に重要)
- MyRunnaクラスを定義し、Runnableインターフェースを実装します。
- run()メソッドを実装し、スレッド実行本体を記述します。
- スレッドオブジェクトを作成し、start()メソッドを呼び出してスレッドを起動します。
package jp.example.threads;
//スレッド作成方法二
//Runnableインターフェースを実装し、runメソッドをオーバーライドします。
//スレッドの実行にはRunnableインターフェースの実装クラスを渡し、startメソッドを呼び出します。
public class RunnableDemo implements Runnable {
@Override
public void run() {
//runメソッドのスレッド本体
for (int i = 0; i < 20; i++) {
System.out.println("コードを確認中 " + i);
}
}
public static void main(String[] args) {
//Runnableインターフェースの実装クラスオブジェクトを作成
RunnableDemo runnable = new RunnableDemo();
//スレッドオブジェクトを作成し、スレッドを開始します。(プロキシ)
/*
Thread thread = new Thread(runnable);
thread.start();
以下に一行で簡略化します。
*/
new Thread(runnable).start();
//メインスレッド
for (int i = 0; i < 20; i++) {
System.out.println("メインスレッドを学習中 " + i);
}
}
}
結果:
メインスレッドを学習中 0
メインスレッドを学習中 1
メインスレッドを学習中 2
メインスレッドを学習中 3
メインスレッドを学習中 4
メインスレッドを学習中 5
メインスレッドを学習中 6
メインスレッドを学習中 7
メインスレッドを学習中 8
メインスレッドを学習中 9
メインスレッドを学習中 10
メインスレッドを学習中 11
メインスレッドを学習中 12
メインスレッドを学習中 13
メインスレッドを学習中 14
メインスレッドを学習中 15
メインスレッドを学習中 16
メインスレッドを学習中 17
メインスレッドを学習中 18
メインスレッドを学習中 19
コードを確認中 0
コードを確認中 1
コードを確認中 2
コードを確認中 3
コードを確認中 4
コードを確認中 5
コードを確認中 6
コードを確認中 7
コードを確認中 8
コードを確認中 9
コードを確認中 10
コードを確認中 11
コードを確認中 12
コードを確認中 13
コードを確認中 14
コードを確認中 15
コードを確認中 16
コードを確認中 17
コードを確認中 18
コードを確認中 19
—Threadクラスの継承とRunnableインターフェースの実装の比較:
-Threadクラスの継承
- サブクラスがThreadクラスを継承するとマルチスレッド能力を持ちます
- スレッド起動:サブクラスオブジェクト.start()
- 使用非推奨:OOPの単一継承の制約を回避するため
-Runnableインターフェースの実装
- Runnableインターフェースを実装するとマルチスレッド能力を持ちます
- スレッド起動:ターゲットオブジェクトを渡す + Threadオブジェクト.start()
- 推奨使用:単一継承の制約を回避し、柔軟で便利、同じオブジェクトを複数のスレッドで簡単に使用できます。
リソースの競合:
同じリソースを操作する場合、リソースの競合問題が発生する可能性があり、同時実行制御を追加する必要があります。
package jp.example.threads;
//複数のスレッドが同じオブジェクトを同時に操作する
//切符を買う例
//問題発見:複数のスレッドが同じリソースを操作すると、スレッドは安全ではなく、スレッドが乱れます。
public class TicketDemo implements Runnable {
//切符の数
private int ticketCount = 10;
@Override
public void run() {
while (true) {
if (ticketCount <= 0) {
break;
}
//Thread.currentThread().getName()で現在のスレッドの名前を取得
System.out.println(Thread.currentThread().getName() + "が" + (ticketCount--) + "番目の切符を取得しました");
}
}
public static void main(String[] args) {
TicketDemo ticketDemo = new TicketDemo();
new Thread(ticketDemo, "窓口1").start();
new Thread(ticketDemo, "窓口2").start();
new Thread(ticketDemo, "窓口3").start();
}
}
結果:
窓口2が10番目の切符を取得しました
窓口2が8番目の切符を取得しました
窓口2が7番目の切符を取得しました
窓口2が6番目の切符を取得しました
窓口3が9番目の切符を取得しました
窓口1が10番目の切符を取得しました
窓口3が4番目の切符を取得しました
窓口2が5番目の切符を取得しました
窓口3が2番目の切符を取得しました
窓口1が3番目の切符を取得しました
窓口2が1番目の切符を取得しました
ウサギとカメのレース:
package jp.example.threads;
//ウサギとカメのレースをシミュレート
public class RaceDemo implements Runnable {
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//レースが終了したかどうかを判断
System.out.println(Thread.currentThread().getName() + "が" + i + "歩進みました");
boolean flag = isGameOver(i);
if (flag) {
break;
}
//ウサギの休憩をシミュレート
if (Thread.currentThread().getName().equals("ウサギ") && i % 10 == 0) {
try {
Thread.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private boolean isGameOver(int steps) {
if (winner != null) {
//すでに勝者がいる
return true;
} else {
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("勝者は" + winner);
}
}
return false;
}
public static void main(String[] args) {
RaceDemo race = new RaceDemo();
new Thread(race, "ウサギ").start();
new Thread(race, "カメ").start();
}
}
結果:
カメが0歩進みました
ウサギが0歩進みました
カメが1歩進みました
カメが2歩進みました
カメが3歩進みました
カメが4歩進みました
カメが5歩進みました
カメが6歩進みました
カメが7歩進みました
カメが8歩進みました
カメが9歩進みました
カメが10歩進みました
カメが11歩進みました
カメが12歩進みました
カメが13歩進みました
カメが14歩進みました
カメが15歩進みました
カメが16歩進みました
カメが17歩進みました
カメが18歩進みました
カメが19歩進みました
カメが20歩進みました
カメが21歩進みました
カメが22歩進みました
カメが23歩進みました
カメが24歩進みました
カメが25歩進みました
カメが26歩進みました
カメが27歩進みました
カメが28歩進みました
カメが29歩進みました
カメが30歩進みました
カメが31歩進みました
カメが32歩進みました
カメが33歩進みました
カメが34歩進みました
カメが35歩進みました
カメが36歩進みました
カメが37歩進みました
カメが38歩進みました
カメが39歩進みました
カメが40歩進みました
カメが41歩進みました
カメが42歩進みました
カメが43歩進みました
カメが44歩進みました
カメが45歩進みました
カメが46歩進みました
カメが47歩進みました
カメが48歩進みました
カメが49歩進みました
カメが50歩進みました
カメが51歩進みました
カメが52歩進みました
カメが53歩進みました
カメが54歩進みました
カメが55歩進みました
カメが56歩進みました
カメが57歩進みました
カメが58歩進みました
カメが59歩進みました
カメが60歩進みました
カメが61歩進みました
カメが62歩進みました
カメが63歩進みました
カメが64歩進みました
カメが65歩進みました
カメが66歩進みました
カメが67歩進みました
カメが68歩進みました
カメが69歩進みました
カメが70歩進みました
カメが71歩進みました
カメが72歩進みました
カメが73歩進みました
カメが74歩進みました
カメが75歩進みました
カメが76歩進みました
カメが77歩進みました
カメが78歩進みました
カメが79歩進みました
カメが80歩進みました
カメが81歩進みました
カメが82歩進みました
カメが83歩進みました
カメが84歩進みました
カメが85歩進みました
カメが86歩進みました
カメが87歩進みました
カメが88歩進みました
カメが89歩進みました
カメが90歩進みました
カメが91歩進みました
カメが92歩進みました
カメが93歩進みました
カメが94歩進みました
カメが95歩進みました
ウサギが1歩進みました
カメが96歩進みました
ウサギが2歩進みました
ウサギが3歩進みました
カメが97歩進みました
ウサギが4歩進みました
カメが98歩進みました
カメが99歩進みました
カメが100歩進みました
ウサギが5歩進みました
勝者はカメ//停滞し続ければ、平凡さが訪れる
-Callableインターフェース
(理解が必要、仕事で必要)