1。プロセスとスレッド
1。プロセスは何ですか?
狭い定義:プロセスは、実行されているコンピュータープログラムのインスタンスです。
一般的な定義:プロセスとは、特定のデータセットに関する特定の独立した機能を備えたプログラムの実行アクティビティです。これは、オペレーティングシステムの動的実行の基本単位です。従来のオペレーティングシステムでは、プロセスは基本的な割り当てユニットと基本的な実行ユニットの両方です。
2。スレッドとは何ですか?
軽量プロセス(LWP)と呼ばれるスレッドは、プログラム実行フローの最小単位です。標準スレッドは、スレッドID、現在の命令ポインター(PC)、レジスタのセット、およびスタックで構成されています。さらに、スレッドはプロセスのエンティティであり、システムによって独立してスケジュールおよびディスパッチされた基本ユニットです。スレッド自体はシステムリソースを所有していませんが、操作中にいくつかの重要なリソースしかありませんが、プロセスが所有するすべてのリソースを同じプロセスに属する他のスレッドと共有できます。
3.プロセスとスレッドの違いは何ですか?
プロセスとスレッドの主な違いは、それらが異なるオペレーティングシステムリソース管理方法であることです。
プロセスには、独立したアドレススペースがあります。プロセスがクラッシュした後、保護されたモードの他のプロセスには影響しません。スレッドはプロセスの異なる実行パスです。
スレッドには独自のスタックとローカル変数がありますが、スレッド間に個別のアドレススペースはありません。スレッドが死んだ場合、それはプロセス全体が死ぬことを意味します。したがって、マルチプロセスプログラムはマルチスレッドプログラムよりも堅牢ですが、プロセスを切り替えると、より多くのリソースを消費し、効率が低くなります。ただし、特定の変数を共有する必要がある同時操作を必要とするいくつかの同時操作では、プロセスではなくスレッドのみを使用できます。
要するに、スレッドとプロセスの違いは次のとおりです。
(1)プログラムには少なくとも1つのプロセスがあり、プロセスには少なくとも1つのスレッドがあります。
(2)スレッドの分割スケールはプロセスのスケールよりも小さく、マルチスレッドプログラムの並行性を高くしています。
(3)プロセスには、実行中に独立したメモリユニットがあり、複数のスレッドがメモリを共有しているため、プログラムの操作効率が大幅に向上します。
(4)実行中にスレッドとプロセスに違いがあります。各独立したスレッドには、プログラム実行のためのエントリ、実行のシーケンス、プログラムの終了があります。ただし、スレッドは個別に実行することはできず、アプリケーションに存在する必要があり、アプリケーションによって複数のスレッド実行コントロールが提供されます。
(5)論理的な観点から、マルチスレッドの意味は、アプリケーションでは、複数の実行部品を同時に実行できるという点にあります。ただし、オペレーティングシステムは、プロセスのスケジューリング、管理、リソースの割り当てを実現するための複数のスレッドを複数の独立したアプリケーションと見なしていません。
これは、プロセスとスレッドの重要な違いです。
2。スレッドと5つの基本状態のライフサイクル
Javaスレッドには5つの基本的な状態があります。
(1)新しい状態(新しい):スレッドオブジェクトペアが作成されると、次のような新しい状態に入ります。
(2)Ready State(実行可能):スレッドオブジェクトのstart()メソッド(t.start();)のstart()メソッドの場合、スレッドはready状態に入ります。準備が整った状態のスレッドは、スレッドの準備ができており、CPUがいつでも実行をスケジュールするのを待っていることを意味します。T.Start()が実行された直後にスレッドが実行されることではありません。
(3)実行状態:CPUが準備ができた状態でスレッドのスケジュールを開始すると、スレッドは本当に実行できます。つまり、実行中の状態に入ります。注:Ready Stateは、実行状態への唯一のエントリです。つまり、スレッドが実行されて実行された状態に入りたい場合、最初に準備ができている必要があります。
(4)ブロック状態:何らかの理由で、実行状態のスレッドが一時的にCPUの使用を放棄し、実行を停止します。この時点で、ブロッキング状態に入ります。実行中の状態に入るために、CPUから再び呼び出される機会はありません。ブロッキングの理由によると、ブロッキング状態は3つのタイプに分けることができます。
blockingブロッキングを待つ:実行中の状態のスレッドは、wait()メソッドを実行して、スレッドがブロッキング状態を待機しているようにします。
sych型ブロッキング:スレッドは、同期化された同期ロックを取得できません(ロックは他のスレッドで占有されているため)、同期されたブロッキング状態に入ります。
その他のブロッキング:スレッドのSleep()またはJoin()を呼び出すとき、またはI/Oリクエストの送信すると、スレッドはブロッキング状態に入ります。 Sleep()状態がタイムアウトしたとき、Join()がスレッドが終了またはタイミングアウトするのを待っているか、I/O処理が完了した場合、スレッドは準備ができた状態に再入力されました。
(5)死んだ状態:スレッドは例外のために実行()メソッドの実行を終了または終了し、スレッドはライフサイクルを終了します。
3。Javaマルチスレッドの実装
Javaでは、マルチスレッドプログラムを実装する場合は、スレッドのメインクラス(スレッドのメインクラスを表すメインクラスの概念など)に依存する必要がありますが、このスレッドのメインクラスには、定義する際には特別な要件が必要です。このクラスは、スレッドクラスを継承するか、実行可能なインターフェイスを実装して定義を完了できます。
1.スレッドクラスを継承して、マルチスレッドを実装します
java.lang.threadは、スレッド操作を担当するクラスです。スレッドクラスを継承する場合、スレッドのメインクラスになることができます。メインクラスであるため、使用方法が必要であり、スレッドによって開始されるメインメソッドは、スレッドクラスでrun()メソッドを上書きする必要があります。
スレッドのボディクラスを定義します。
クラスMythreadはスレッドを拡張します{//スレッドプライベート文字列タイトルのメインクラス。 public mythread(string title){this.title = title; } @Override public void run(){//(int x = 0; x <10; x ++){system.out.println(this.title + "run、x =" + x); }}}スレッドクラスがあり、対応する操作方法があるため、オブジェクトを生成し、内部のメソッドを呼び出す必要があるため、次のプログラムが記述されます。
public class testdemo {public static void main(string [] args){mythread mt1 = new mythread( "thread a"); mythread mt2 = new mythread( "スレッドB"); mythread mt3 = new mythread( "スレッドC"); mt1.run(); mt2.run(); mt3.run(); }実行結果:
スレッドa run、x = 0
スレッドAの実行、x = 1
スレッドa run、x = 2
スレッドa run、x = 3
スレッドAの実行、x = 4
スレッドAの実行、x = 5
スレッドAの実行、x = 6
スレッドAの実行、x = 7
スレッドAの実行、x = 8
スレッドAの実行、x = 9
スレッドBが実行されます、x = 0
スレッドBが実行されます、x = 1
スレッドBは実行されます、x = 2
スレッドBは実行されます、x = 3
スレッドBは実行されます、x = 4
スレッドBは実行されます、x = 5
スレッドBは実行されます、x = 6
スレッドBが実行されます、x = 7
スレッドBは実行されます、x = 8
スレッドBが実行されます、x = 9
スレッドCが実行されます、x = 0
スレッドCは実行されます、x = 1
スレッドCは実行されます、x = 2
スレッドCは実行されます、x = 3
スレッドCが実行されます、x = 4
スレッドCが実行されます、x = 5
スレッドCが実行されます、x = 6
スレッドCが実行されます、x = 7
スレッドCが実行されます、x = 8
スレッドCが実行されます、x = 9
複数のスレッドの実行を交互に実行する必要があるため、上記の操作は実際にはマルチスレッドを開始しないことがわかりました。この時点では、各オブジェクトのコードが実行された後も下向きに実行され続けます。
プログラムでマルチスレッドを真に開始したい場合は、スレッドクラスのメソッド:public void start()に依存する必要があります。つまり、マルチスレッドを実際に開始します。このメソッドを呼び出した後、run()メソッドは間接的に呼び出されます。
public class testdemo {public static void main(string [] args){mythread mt1 = new mythread( "thread a"); mythread mt2 = new mythread( "スレッドB"); mythread mt3 = new mythread( "スレッドC"); mt1.start(); mt2.start(); mt3.start(); }}実行結果:
スレッドCが実行されます、x = 0
スレッドa run、x = 0
スレッドBが実行されます、x = 0
スレッドAの実行、x = 1
スレッドCは実行されます、x = 1
スレッドa run、x = 2
スレッドBが実行されます、x = 1
スレッドa run、x = 3
スレッドAの実行、x = 4
スレッドAの実行、x = 5
スレッドCは実行されます、x = 2
スレッドCは実行されます、x = 3
スレッドCが実行されます、x = 4
スレッドCが実行されます、x = 5
スレッドCが実行されます、x = 6
スレッドCが実行されます、x = 7
スレッドCが実行されます、x = 8
スレッドCが実行されます、x = 9
スレッドAの実行、x = 6
スレッドAの実行、x = 7
スレッドAの実行、x = 8
スレッドAの実行、x = 9
スレッドBは実行されます、x = 2
スレッドBは実行されます、x = 3
スレッドBは実行されます、x = 4
スレッドBは実行されます、x = 5
スレッドBは実行されます、x = 6
スレッドBが実行されます、x = 7
スレッドBは実行されます、x = 8
スレッドBが実行されます、x = 9
この時点で、複数のスレッドが互いに交互に実行されることがわかりますが、各実行の結果は異なります。上記のコードを介して結論を描くことができます。スレッドを起動する場合は、スレッドクラスのstart()メソッドに頼って実行する必要があります。スレッドが開始されると、run()メソッドはデフォルトで呼び出されます。
start()メソッドを呼び出した後、一連の複雑なことが起こりました。
(1)新しい実行スレッドを起動します(新しいコールスタックを使用)。
(2)スレッドは新しい状態から実行可能状態に転送されます。
(3)スレッドが実行する機会を取得すると、ターゲットrun()メソッドが実行されます。
注:Javaの場合、run()メソッドには特別なものはありません。 Main()メソッドと同様に、新しいスレッドが呼び出しのメソッド名(および署名)を知っていることを意味します。したがって、実行可能またはスレッドで実行方法を呼び出すことは合法ですが、新しいスレッドを起動しません。
説明:スレッドが起動したときに直接呼び出すのではなく、なぜstart()を呼び出す必要があるのですか?
Start()を呼び出した後、実際にOverridden run()メソッドを実行することがわかりました。この問題を説明するには、スレッドクラスのソースコードを開き、start()メソッドの定義を観察します。
public synchronized void start(){if(threadStatus!= 0)throw new IllegalThreadStateException(); group.add(this); boolean start = false; try {start0(); start = true; }最後に{try {if(!start){group.threadstartfailed(this); }} catch(Throwable Ingrore){}}}プライベートネイティブvoid start0();このメソッドのソースコードを開くと、最初にメソッドが「IllegalThreadStateException」例外をスローすることがわかります。一般的に言えば、メソッドがスローを使用して例外オブジェクトをスローする場合、この例外はtry ... catchを使用してキャッチするか、メソッド宣言のスローを使用してスローする必要がありますが、この領域には何もありません。なぜ?この例外クラスは、ランタイム例外のサブクラス(runtimeexception)に属しているためです。
java.lang.object
| -java.lang.throwable
| -java.lang.exception
| -java.lang.runtimeexception
| -java.lang.illegalargumentexception
| -java.lang.illegalthreadstateException
この例外は、スレッドオブジェクトが繰り返し開始されるとスローされます。つまり、スレッドオブジェクトは1回しか開始できません。
start()メソッドの最も重要な部分の1つはstart0()メソッドです。この方法では、ネイティブキーワード定義を使用します。
ネイティブキーワードは、Javaネイティブインターフェイスを指します。つまり、Javaは、ネイティブオペレーティングシステムの関数関数を呼び出して、特別な操作を完了するために使用されます。 Javaの最大の特徴は携帯性であるため、このようなコード開発はJavaではほとんど見られません。プログラムが固定オペレーティングシステムでのみ使用できる場合、移植性は完全に失われるため、この操作は一般的に使用されません。
マルチスレッドの実装には、オペレーティングシステムのサポートが必要です。次に、上記のstart0()メソッドは、実際にはメソッド本体のない抽象的なメソッドと非常に似ています。このメソッド本体はJVMに引き渡されて実装されます。つまり、WindowsのJVMはAメソッドを使用してstart0()を実装できますが、LinuxのJVMはbメソッドを使用してStart0()を実装できますが、呼び出しの場合、Start0()メソッドを実装する特定の方法を気にしませんが、最終操作の結果のみを気にし、JVMに格の操作を行います。
したがって、マルチスレッド操作では、start()メソッドを使用してマルチスレッド操作を開始するには、オペレーティングシステム機能呼び出しが必要です。
2。実行可能なインターフェイスを実装して、マルチスレッドを実装します
スレッドクラスを使用すると、実際にマルチスレッドの実装が容易になりますが、この方法の最大の欠点は単一の継承の問題です。この目的のために、実行可能なインターフェイスをJavaで使用してマルチスレッドを実装することもできます。このインターフェイスの定義は次のとおりです。
public interface runnable {public void run();}実行可能なインターフェイスを介してマルチスレッドを実装します。
クラスMythreadは実行可能{//スレッドのメインクラスのプライベート文字列タイトルを実装します。 public mythread(string title){this.title = title; } @Override public void run(){//(int x = 0; x <10; x ++){system.out.println(this.title + "run、x =" + x); }}}これは、スレッドクラスを継承する以前の方法とそれほど違いはありませんが、1つの利点は、単一の継承の制限を回避することです。
しかし、問題はここにあります。前述のように、マルチスレッドを開始したい場合は、スレッドクラスのstart()メソッドに依存する必要があります。スレッドクラスを継承すると、この方法を直接継承して使用できます。しかし、今では実行可能なインターフェイスを実装しています。この方法がなければ、継承できます。何をすべきですか?
この問題を解決するには、スレッドクラスに頼ってそれを完了する必要があります。コンストラクターは、実行可能なインターフェイスオブジェクトを受信するためにスレッドクラスで定義されています。
パブリックスレッド(実行可能ターゲット);
スレッドクラスを使用してマルチスレッドを開始します。
public class testdemo {public static void main(string [] args)スロー例外{mythread mt1 = new mythread( "thread a"); mythread mt2 = new mythread( "スレッドB"); mythread mt3 = new mythread( "スレッドC");新しいスレッド(mt1).start();新しいスレッド(mt2).start();新しいスレッド(mt3).start(); }}実行結果:
スレッドa run、x = 0
スレッドBが実行されます、x = 0
スレッドBが実行されます、x = 1
スレッドCが実行されます、x = 0
スレッドBは実行されます、x = 2
スレッドAの実行、x = 1
スレッドBは実行されます、x = 3
スレッドCは実行されます、x = 1
スレッドCは実行されます、x = 2
スレッドBは実行されます、x = 4
スレッドBは実行されます、x = 5
スレッドa run、x = 2
スレッドa run、x = 3
スレッドAの実行、x = 4
スレッドAの実行、x = 5
スレッドAの実行、x = 6
スレッドAの実行、x = 7
スレッドAの実行、x = 8
スレッドAの実行、x = 9
スレッドBは実行されます、x = 6
スレッドBが実行されます、x = 7
スレッドBは実行されます、x = 8
スレッドBが実行されます、x = 9
スレッドCは実行されます、x = 3
スレッドCが実行されます、x = 4
スレッドCが実行されます、x = 5
スレッドCが実行されます、x = 6
スレッドCが実行されます、x = 7
スレッドCが実行されます、x = 8
スレッドCが実行されます、x = 9
現時点では、マルチスレッドスタートアップが達成されるだけでなく、単一の相続限界も達成されていません。
3.マルチスレッドを実装する3番目の方法:呼び出し可能なインターフェイスを使用してマルチスレッドを実装する
実行可能なインターフェイスを使用したマルチスレッドは、単一の継承の制限を回避できますが、実行可能なインターフェイスのrun()メソッドが操作結果を返すことができないという問題があります。この問題を解決するために、新しいインターフェイスが提供されます:呼び出し可能なインターフェイス(java.util.concurrent.callable)。
パブリックインターフェイスCallable <v> {public v Call()Throws Exception;}呼び出し可能なインターフェイスでcall()メソッドを実行した後、結果が返されます。返される結果のタイプは、呼び出し可能なインターフェイス上のジェネリックによって決定されます。
マルチスレッドを実装するために呼び出し可能なインターフェイスを実装する特定の操作は次のとおりです。
呼び出し可能なインターフェイスの実装クラスを作成し、clall()メソッドを実装します。次に、FutureTaskクラスを使用して、呼び出し可能な実装クラスのオブジェクトをラップし、このFutureTaskオブジェクトをスレッドオブジェクトのターゲットとして使用してスレッドを作成します。
スレッドボディクラスを定義します:
Import java.util.concurrent.callable; class mythreadを実装する<string> {private int ticket = 10; @Override public String call()throws Exception {for(int i = 0; i <20; i ++){if(this.ticket> 0){system.out.println( "販売チケット、残りの投票数は"+this.ticket-); }} return "チケットは完売しました"; }}スレッドクラスは、呼び出し可能なインターフェイスを直接サポートしていません。 JDK1.5の後、クラスが提供されます:
java.util.concurrent.futuretask <v>
このクラスは、主に呼び出し可能なインターフェイスオブジェクトの操作を担当しています。その定義構造は次のとおりです。
パブリッククラスFutureTask <v>
オブジェクトを拡張します
RunnableFurture <v>を実装します
RunnableFurtureインターフェイスには、次の定義があります。
パブリックインターフェイスRunnableFurture <V>
実行可能、未来<v>を拡張します
次のコンストラクターは、FutureTaskクラスで定義されています。
Public FutureTask(呼び出し可能<v>呼び出し可能)
これで、最終的にFutureTaskクラスを介して呼び出し可能なインターフェイスオブジェクトを受信できます。受信の目的は、call()メソッドの返品結果を取得することです。
上記の分析から見つけることができます:
FutureTaskクラスは呼び出し可能なインターフェイスオブジェクトを受信でき、FutureTaskクラスは実行可能なインターフェイスを継承する実行可能なフルチャーインターフェイスを実装します。
だから、私たちはこのようなマルチスレッドを始めることができます:
public class testdemo {public static void main(string [] args)throws exception {mythread mt1 = new mythread(); mythread mt2 = new mythread(); FutureTask <String> task1 = new FutureTask <String>(mt1); // call()メソッドを取得して結果を返すfutureTask <string> task2 = new futureTask <string>(mt2); // get call()メソッドは結果を返します// futureTaskは実行可能なインターフェイスのサブクラスです。スレッドクラスの構成を使用して、タスクオブジェクトを受信できますnewスレッド(task1).start();新しいスレッド(task2).start(); // MultiThRead実行が完了したら、FutureTaskの親インターフェイスのFutureでget()メソッドを使用して、実行結果system.out.println( "スレッド1の戻り結果:"+task1.get())を取得できます。 system.out.println( "スレッド2の返品結果:"+task2.get()); }}実行結果:
チケットを販売すると、残りの投票数は10です
チケットを販売すると、残りの投票数は10です
チケットを販売すると、残りの投票数は9です
チケットを販売すると、残りの投票数は8です
チケットを販売すると、残りの投票数は7です
チケットを販売すると、残りの投票数は9です
チケットを販売すると、残りの投票数は6です
チケットを販売すると、残りの投票数は8です
チケットを販売すると、残りの投票数は5です
チケットを販売すると、残りの投票数は7です
チケットを販売すると、残りの投票数は4です
チケットを販売すると、残りの投票数は6です
チケットを販売すると、残りの投票数は3です
チケットを販売すると、残りの投票数は5です
チケットを販売すると、残りの投票数は2です
チケットを販売すると、残りの投票数は4です
チケットを販売すると、残りの投票数は1です
チケットを販売すると、残りの投票数は3です
チケットを販売すると、残りの投票数は2です
チケットを販売すると、残りの投票数は1です
スレッド1の返品結果:チケットは完売しました。スレッド2の返品結果:チケットは完売しました。
まとめ:
上記では、マルチスレッドを実装する3つの方法を説明しています。スレッドスタートアップの場合、それらはすべてスレッドオブジェクトのstart()メソッドと呼ばれます。 Start()メソッドを同じスレッドオブジェクトで2回呼び出すことはできないことに注意することが重要です。