1.プロセスとスレッド
プロセスとスレッドの起源:
コンピューターCPUの動作原理であるオペレーティングシステムは、シングルコアCPUをスケジュールするときに、実際には常にタスクを切り替えます。これは、CPUが同時に実行できるのは1つだけであり、CPUは非常に高速であり、他のCPUを待機する必要があるためです。タスク完了時のパーツタスク(ハードディスクの読み取りおよび書き込みI / Oブロッキングなど)を完了するために、CPUは、現時点でリソースを最大化するために、異なるタスク間でのみ切り替えを行うことができます。これを同時実行と呼びます。
しかし、並行性によっていくつかの問題が発生しました。元の単一のタスクが複数のタスクになりました。タスクを切り替える方法は?この時点で、プロセスの概念が導入されています。
プロセスを使用してプログラムに対応します。各プロセスは特定のメモリアドレス空間に対応し、独自のメモリ空間のみを使用でき、各プロセスは相互に干渉しません。また、プロセスは各瞬間のプログラムの実行状態を保存します。これにより、プロセスを切り替えることができます。
プロセスが一時的な場合は、現在のプロセスの状態(プロセスID、プロセスで使用されているリソースなど)を保存し、次に戻ると、以前に保存した状態に従って復元し、その後、実行を継続します。これは同時実行性であり、オペレーティングシステムは、複数のタスクが同じ期間に実行されていることを巨視的に見ることができます。
言い換えると、このプロセスにより、オペレーティングシステムの同時実行が可能になります。
スレッドはプロセスのサブセットであり、スレッドはアプリケーションの最小の実行ユニットでもあります。
プロセスの出現はオペレーティングシステムの並行性の問題を解決しますが、プロセスの場合、読み取り、コンピューティング、リアルタイム表示などの複数のサブタスクも実行する必要があります。効率を最大化するために、人々は概念を導入しますスレッドの。プロセスはオペレーティングシステムの並行性を可能にし、スレッドはプロセスの内部並行性を可能にします。
プロセスには複数のスレッドが含まれていますが、これらのスレッドはプロセスが占めるリソースとアドレス空間を共有していることに注意してください。プロセスは、リソース割り当てのためのオペレーティングシステムの基本単位であり、スレッドは、スケジューリングのためのオペレーティングシステムの基本単位です。
2.プロセスとスレッドの違い
-
スレッドはプログラム実行の最小単位であり、プロセスはオペレーティングシステムによるリソースの割り当ての最小単位です。
-
プロセスは、プロセス内のコードの異なる実行ルートである1つ以上のスレッドで構成されます
-
プロセスは互いに独立していますが、同じプロセス内の各スレッドは、プログラムのメモリスペース(コードセグメント、データセット、ヒープなどを含む)と一部のプロセスレベルのリソース(開いているファイルやシグナルなど)を共有します。 。)スレッドは他のプロセスでは表示されません。
-
スケジューリングと切り替え:スレッドコンテキストの切り替えは、プロセスコンテキストの切り替えよりもはるかに高速です
人生の例:オンラインのときは、QQを切り、iQiyiを見て、Thunderを使用してファイルをダウンロードします。現時点では、QQ、iQiyi、およびXunleiをプロセスと見なすことができます。また、QQでZhang San、Li Siなどと同時にチャットすることができ、Zhang San、LiSiなどのチャットインターフェイスはスレッドになります。
質問:マルチスレッドのパフォーマンスは、必然的にシングルスレッドによるものですか?
必ずしもそうとは限りませんが、特定のタスクとコンピューターの構成によって異なります。例:シングルコアCPUの場合、ファイルの解凍など、CPUを集中的に使用するタスクの場合、マルチスレッドのパフォーマンスはシングルスレッドのパフォーマンスほど良くありません。
ファイルの解凍は常にCPUリソースを占有する必要があるため、マルチスレッドを使用すると、スレッドの切り替えによって発生するオーバーヘッドによって実際にパフォーマンスが低下します。ただし、インタラクティブタスクなどのタスクの場合は、マルチスレッドを使用する必要があります。マルチコアCPUの場合、ファイルの解凍にはシングルスレッドよりもマルチスレッドの方が確実に優れています。これは、複数のスレッドが各コアのリソースをより十分に活用できるためです。
マルチスレッドはプログラムのパフォーマンスを向上させることができますが、そのプログラミングはシングルスレッドよりもはるかに複雑であり、スレッドセーフの問題を考慮する必要があります。
したがって、実際のプログラミングプロセスでは、実際の状況に応じて特定の選択を行う必要があります。
3.Javaでのスレッド作成メソッド
前回の記事では、スレッドの概念について予備的な理解がありましたが、JVMでスレッドを作成するにはどうすればよいでしょうか。
Javaでスレッドを作成するには、一般に2つの方法があります。1)Threadクラスを継承します。2)Runnableインターフェイスを実装します。
-
1.Threadクラスを継承してスレッドを作成します
/** * 1 .继承Thread类 * 继承Thread类的话,必须重写run方法,在run方法中定义需要执行的任务。 **/ class ThreadOne extends Thread{ @Override public void run(){ System.out.println(Thread.currentThread().getId()+"开始运行 了"); } } /** * 创建好了自己的线程类之后,就可以创建线程对象了,然后通过start()方法去启动线 程。 **/ public static void main(String[] args) throws Exception{ new ThreadOne().start(); //jdk1.8下用 lambda表达式简化写法 new Thread(()-> System.out.println(Thread.currentThread().getId()+"开始运行 了")).start(); }
スレッドクラスが作成された後、スレッドは現時点では単なる別のJavaオブジェクトであり、スレッドと呼ぶことはできません。スレッドは、start()メソッドを呼び出すことによってのみ開始できます。注:run()メソッドを呼び出してスレッドを開始する代わりに、runメソッドは実行する必要のあるタスクのみを定義します。runメソッドが呼び出された場合、のThreadOneオブジェクトでrun()メソッドを実行するのと同じです。メインスレッドであり、通常のメソッド呼び出しとは関係ありません。違いは、現時点では、定義されたタスクを実行するための新しいスレッドが作成されないことです。
- Runnableインターフェースを実装してスレッドを作成する
JavaはRunnableインターフェースを定義します。インターフェースを実装し、インターフェースでrun()メソッドを書き直すことで、スレッドの作成を実現できます。
//自定义一个Runnable接口的实现类,并重写其中run方法
class ThreadTwo implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getId()+"开始运行了");
}
}
public static void main(String[] args) throws Exception{
//创建Thread类并将接口实现做入参,并调用start方法
new Thread(new ThreadTwo()).start();
}
jdkソースコードを見ると、Runnableインターフェース構造が非常に単純であることがわかります。これは、パラメーターや戻り値のないrunメソッドを定義するだけです。
したがって、厳密な意味では、Runnableインターフェイスを実装してスレッドを作成することは、一種の不正確な書道です。jdkでは、Threadクラスのみがスレッドを表します。スレッドの実行ユニットはrunメソッドです。Thread.runソースコードを確認してください。 。
runメソッドは、ターゲットでrunメソッドを実行するだけであり、ターゲットは何であるかがわかります。Threadのコンストラクターを見ると、次のことがわかります。
ここでのターゲットは、渡したRunnableインターフェイスの実装です。Runnableのインスタンスは、スレッドの実行ユニットのロジックのみを提供します。
したがって、スレッドを作成する方法は2つあります。1つはThread実装クラスを作成する方法、もう1つはRunnableインターフェイスを実装する方法です。このステートメントは厳密ではありません。
厳密に言えば、スレッドを作成する唯一の方法はThreadクラスを構築することであり、スレッドの実行ユニットを実装する方法は2つあります。1つはThreadのrunメソッドを書き直すこと、もう1つはrunを実装することです。 Runnableインターフェースのメソッド。Runnableインターフェースのインスタンスを入力パラメーターとして使用し、Threadインスタンスを作成します。
4.スレッドのライフサイクルの詳細な説明
プログラム実行の最小単位として、スレッドには完全なライフサイクルもあります。
上の図に示すように、スレッドのライフサイクルは大きく次の5つの部分に分けられます。
新規、実行可能、実行中、ブロック済み、終了済み
以下は、各状態の詳細な説明です
-
新しいステータス
new Thread()などのスレッドオブジェクトを作成するとき、スレッドはまだstartメソッドの呼び出しを開始していないため、この時点でスレッドはNEW状態になっています。実際、正確には、現時点ではスレッドは存在しません。start()メソッドを呼び出す前に、キーワードnewを使用して通常のJavaオブジェクトを作成しました。
new状態は、start()によってRUNNABLE状態に入ることができます。
-
RUNNABLE状態
スレッドオブジェクトの作成後、RUNNABLE状態に変更する場合は、start()メソッドを呼び出す必要があります。このメソッドを呼び出した後、JVMプロセスでスレッドを実際に作成できます。ただし、スレッドが開始されるとすぐには実行されません。スレッドの実行は、プロセスと同様にCPUのスケジューリングに依存します。この実行待機状態をRUNNABLE(実行可能)状態と呼びます。実行の資格がありますが、実際にはまだ実行されていません。
この時点ではスレッドは実行状態ではないため、この状態のスレッドは直接BLOCKEDおよびTERMINATED状態になりません。スレッドの実行中に待機、スリープ、またはその他のブロックIO操作が呼び出された場合でも、スレッドはCPUを取得しました。スケジューリング実行権により状態を変更できます。
RUNNABLEスレッドは、予期せず終了するか、RUNNING状態に入るだけです。
-
実行状態
CPUがポーリングやその他の方法でタスク実行可能キューからスレッドを選択すると、この時点でCPUは実際に独自の内部ロジックコードを実行でき、この時点でのスレッドの状態はRUNING状態です。RUNNING状態のスレッドは実際にはRUNNABLEであると言えますが、その逆は当てはまりません。
RUNNING状態でのスレッド状態の一般的な遷移:
-
RUNNINGがBLOCK状態になります
1. sleepメソッドとwaitメソッドを呼び出し、waitSetに入りました。
2.ネットワークデータの読み取りや書き込みなどのブロッキングIO操作を実行します
3.特定のロックリソースを取得するために、それはロックのブロッキングキューに追加されます
-
RUNNINGがRUNNABLE状態になります
1. CPUスケジューラのポーリングにより、スレッドは実行を断念しました
2.スレッドはyieldメソッドをアクティブに呼び出し、CPUの実行権を放棄しました
-
RUNNINGがTERMINATED状態になります
1.stopメソッドを呼び出します
-
-
BLOCKED状態
スレッドは何らかの理由でブロック状態になります。詳細については、スレッドがブロック状態で実行できる状態スイッチを参照してください。
-
RUNNABLE状態に入る
1.スレッドは指定された時間スリープを終了しました
2.取得したいデータの読み取りなど、スレッドブロック操作の終了
3.待機中のスレッドは、他のスレッドnotify / notifyallによってウェイクアップされます
4.スレッドが特定のロックリソースを取得しました
5.スレッドはブロッキングプロセスで中断されます。たとえば、他のスレッドは割り込みメソッドを呼び出します
-
TERMINATED状態に入る
通話停止または予期しない死亡(JVMクラッシュ)
-
-
終了ステータス
スレッドの最終状態この状態のスレッドは状態が変化しません。つまり、ライフサイクル全体が終了します。スレッドはTERMINATED状態になります。
1.スレッドは正常に実行され、ライフサイクルを終了します
2.スレッド実行エラーが予期せず終了しました
3. JVMがクラッシュし、すべてのスレッドが終了します
スレッドのライフサイクルのさまざまな状態間の遷移を理解することは非常に重要です。各言語によって具体的に定義された状態列挙は異なる場合がありますが、一般に、これらの5つの状態の範囲に含まれます。