スレッドステータス図
スレッドには、次の5つの状態が含まれます。
1。新しい状態:スレッドオブジェクトが作成された後、新しい状態に入ります。たとえば、スレッドスレッド= newスレッド()。
2。実行可能:「実行可能状態」とも呼ばれます。スレッドオブジェクトが作成された後、他のスレッドはオブジェクトのstart()メソッドを呼び出してスレッドを開始します。たとえば、thread.start()。準備ができた状態のスレッドは、いつでもCPUによって実行されるようにスケジュールされる場合があります。
3。実行状態(実行中):スレッドは実行のCPUアクセス許可を取得します。スレッドは、既製の状態からのみ実行状態に入ることができることに注意する必要があります。
4。ブロック状態:ブロック状態とは、スレッドが何らかの理由でCPUの使用権を放棄し、一時的に実行を停止することを意味します。スレッドが準備が整った状態に入るまで、実行中の状態に行く機会がありません。閉塞には3つのタイプがあります。
(01)ブロックを待つ - スレッドのwait()メソッドを呼び出すことにより、スレッドが特定の作業の完了を待ってください。
(02)同期されたブロッキング - スレッドは、同期化された同期ロックを取得できません(ロックは他のスレッドで占有されているため)、同期されたブロッキング状態に入ります。
(03)その他のブロッキング - スレッドは、スレッドのSleep()またはJoin()を呼び出すか、I/O要求を発行することにより、ブロッキング状態を入力します。 Sleep()状態がタイムアウトしたとき、Join()がスレッドが終了またはタイミングアウトするのを待っているか、I/O処理が完了した場合、スレッドは準備ができた状態に再入力されました。
5。DeadState:スレッドは、例外のために実行()メソッドの実行を終了または終了し、スレッドはライフサイクルを終了します。
マルチスレッドメソッドスレッドと実行可能な実装
スレッド:スレッドクラスを継承し、実行方法を実装し、メイン関数の開始メソッドを呼び出してスレッドを開始する
実行可能:インターフェイス、実行可能なインターフェイスを実装し、それをスレッドコンストラクターにパラメーターとして渡し、メインの開始方法を呼び出します
例:
クラスタスクは実行可能{private intチケット= 10; @Override public void run(){for(int i = 0; i <20; i ++){if(this.ticket> 0){system.out.println(thread.currentthread()。 }}}}}}; public class runnabletest {public static void main(string [] args){task mytask = new task();スレッドT1 =新しいスレッド(myTask);スレッドT2 =新しいスレッド(myTask);スレッドT3 =新しいスレッド(myTask); t1.start(); t2.start(); t3.Start(); }} // threadtest.javaソースコードクラスmythread extends thread {private int ticket = 10; public void run(){for(int i = 0; i <20; i ++){if(this.ticket> 0){system.out.println(this.getName() + "チケット販売:チケット" + thisict--); }}}} public class threadtest {public static void main(string [] args){// 3スレッドT1、T2、T3を開始します。各スレッドはそれぞれ10枚のチケットを販売しています! mythread t1 = new mythread(); mythread t2 = new mythread(); mythread t3 = new mythread(); t1.start(); t2.start(); t3.Start(); }};
スレッドと実行可能な違い
スレッドはクラスであり、実行可能はインターフェイスです。スレッド自体は、実行可能なインターフェイスを実装するクラスです。 「クラスには1つの親クラスしか持てないが、複数のインターフェイスを実装できる」ことがわかっているため、Runnableのスケーラビリティが優れていることがわかっています。さらに、Runnableは「リソースの共有」にも使用できます。つまり、特定の実行可能なオブジェクトに基づいて複数のスレッドが作成され、実行可能なオブジェクトでリソースを共有します。一般的に、「Runnable」を介してマルチスレッドを実装することをお勧めします!
スレッドの実行と開始
start():その関数は新しいスレッドを起動することであり、新しいスレッドは対応するrun()メソッドを実行します。 start()を繰り返し呼ぶことはできません。 start()は、実際にローカルメソッドstart0()を介してスレッドを開始します。 start0()は新しいスレッドを実行し、新しいスレッドはrun()メソッドを呼び出します。
run():run()は、通常のメンバーメソッドと同じように繰り返し呼ぶことができます。 run()を個別に呼び出すと、run()が現在のスレッドで実行され、新しいスレッドは開始されません! run()は、スレッドスレッドの実行可能なメンバーのrun()メソッドを直接呼び出すことであり、新しいスレッドは作成されません。
// demo.javaのソースコードクラスmythread extends thread {public mythread(string name){super(name); } public void run(){system.out.println(thread.currentthread()。getname()+"is running"); }}; public class demo {public static void main(string [] args){thread mythread = new mythread( "mythread"); system.out.println(thread.currentthread()。getname()+"call mythread.run()"); mythread.run(); system.out.println(thread.currentthread()。getname()+"call mythread.start()"); mythread.start(); }}出力:
メインコールmythread.run()mainはrunningmain call mythread.start()mythreadが実行されています
同期
Javaでは、各オブジェクトには同期ロックがあります。オブジェクトの同期メソッドを呼び出すと、オブジェクトロックが取得され、同期(OBJ)が「OBJオブジェクト」の同期ロックを取得します。同期ロックへの異なるスレッドアクセスは相互に排他的です。オブジェクトの同期ロックは、特定の時間に1つのスレッドでのみ取得できます。同期ロックを介して、複数のスレッドの「オブジェクト/メソッド」への相互に排他的なアクセスを実現できます。たとえば、2つのスレッドAとスレッドBがあり、すべて「オブジェクトOBJの同期ロック」にアクセスします。ある時点で、スレッドAが「OBJの同期ロック」を取得し、いくつかの操作を実行するとします。この時点で、スレッドBは「OBJの同期ロック」を取得しようとします - スレッドBは取得できません。スレッドAが「OBJの同期ロック」をリリースし、実行することのみを待つ必要があります。
基本的なルール
第1条:スレッドが「同期されたメソッド」または「特定のオブジェクト」の「同期コードブロック」にアクセスすると、他のスレッドは「オブジェクト」の「同期メソッド」または「同期コードブロック」へのアクセスからブロックされます。
第2条:スレッドが「特定のオブジェクト」の「同期メソッド」または「同期コードブロック」にアクセスすると、他のスレッドは「このオブジェクト」の非同期コードブロックにアクセスできます。
第3条:スレッドが「特定のオブジェクト」の「同期メソッド」または「同期コードブロック」にアクセスすると、他のスレッドが「オブジェクト」の他の「同期メソッド」または「同期コードブロック」にアクセスすることをブロックします。
同期された方法
public synchronized void foo1(){system.out.println( "synchronized method");}同期コードブロックpublic void foo2(){synchronized(this){system.out.println( "synchronized method"); }}これは、同期されたコードブロックで、現在のオブジェクトを指します。これは、OBJに置き換えられるなど、他のオブジェクトに置き換えることもできます。FOO2()は、同期(OBJ)の場合、OBJの同期ロックを取得します。
同期されたコードブロックは、競合制限のあるアクセス領域をより正確に制御し、より効率的に実行することができます
インスタンスロックとグローバルロック
インスタンスロック - インスタンスオブジェクトにロックされています。クラスがシングルトンの場合、ロックにはグローバルロックの概念もあります。同期されたキーワードは、インスタンスロックに対応します。
グローバルロック - このロックはクラスを対象としています。インスタンスがいくつのオブジェクトであっても、スレッドはロックを共有します。グローバルロックは、静的同期に対応しています(またはこのクラスのクラスまたはクラスローダーオブジェクトにロックされています)。
pulbic class Something {public synchronized void issynca(){} public synchronized void issyncb(){} public static synchronized void csynca(){} public static synchronized void csyncb(){}}}}
(01)x.issynca()およびx.issyncb()に同時にアクセスできません。 Issynca()とIssyncb()は両方とも同じオブジェクトにアクセスする同期ロックであるためです(オブジェクトX)!
(02)x.issynca()とy.issynca()に同時にアクセスできます。同じオブジェクトの同期ロックにアクセスしていないため、x.issynca()はxの同期ロックにアクセスし、y.issynca()はyの同期ロックにアクセスします。
(03)x.csynca()およびy.csyncb()に同時にアクセスできません。 csynca()とcsyncb()はどちらも静的タイプであるため、x.csynca()はsoments.issynca()と同等であり、y.csyncb()はsoments.issyncb()と同等であり、同期ロックを共有し、同時に質問することはできません。
(04)x.issynca()およびsoments.csynca()に同時にアクセスできます。 Issynca()はインスタンスメソッドであるため、x.issynca()はオブジェクトxのロックを使用します。 csynca()は静的な方法ですが、何か。csynca()は、それが使用されている「クラスロック」であることを理解できます。したがって、それらに同時にアクセスできます。
スレッドブロッキングとウェイクアップ待機、Notify、NotifyAll
object.javaでは、wait()、notify()、notifyall()などのインターフェイスが定義されています。 wait()の関数は、現在のスレッドが待機状態に入るようにすることです。また、wait()は、現在のスレッドが保持されるロックを解放します。 notify()およびnotifyall()の役割は、現在のオブジェクトの待機中のスレッドを覚ますことです。 notify()は単一のスレッドを起動するのに対し、notifyall()はすべてのスレッドを起動することです。
オブジェクトクラスの待機/覚醒に関するAPIの詳細は次のとおりです。
notify() - このオブジェクトモニターを待っている単一のスレッドを起動します。
notifyall() - このオブジェクトモニターで待機しているすべてのスレッドを起動します。
wait() - 現在のスレッドを「wait(blocking)state」に入れ、「他のスレッドがこのオブジェクトのnotify()メソッドまたはnotifyall()メソッドを呼び出すまで」、現在のスレッドが目覚めます(「ready状態」に入力されます)。
待機(長いタイムアウト) - 現在のスレッドを「待機(ブロック)状態」に入れ、「他のスレッドがオブジェクトのnotify()メソッドまたはnotifyAll()メソッドを呼び出すか、指定された時間を超えるまで」にし、現在のスレッドが目覚めます(「ready状態」に入力されます)。
待機(Long Timeout、int nanos) - 現在のスレッドを「待機(ブロック)状態」にし、「他のスレッドがオブジェクトのnotify()メソッドを呼び出すまで、または他のスレッドが現在のスレッドを中断するか、ある程度の時間を超えています」とし、現在のスレッドは目覚めます(「ready state」に入力)。
// waittest.javaのソースコードクラスのスレッドはスレッド{public threada(string name){super(name); } public void run(){synchronized(this){system.out.println(thread.currentthread()。getname()+"call notify()"); //現在の待機スレッドnotify(); }}} public class waittest {public static void main(string [] args){threada t1 = new shooda( "t1");同期(t1){try {// "thread t1" system.out.println(thread.currentthread()。getname()+"start t1"); t1.start(); //メインスレッドは、T1がnotify()を介して目を覚ますのを待ちます。 system.out.println(thread.currentthread()。getname()+"wait()"); t1.wait(); system.out.println(thread.currentthread()。getname()+"continue"); } catch(arturnedexception e){e.printstacktrace(); }}}}出力
メインスタートt1main wait()t1 call notify()main Continue
(01)図の「メインスレッド」は「メインスレッドメイン」を表すことに注意してください。 「スレッドT1」は、「スレッドT1」がWaittestで開始されることを表します。 「ロック」は、「オブジェクトT1の同期ロック」を表します。
(02)「メインスレッド」は、新しいスレッド(「T1」)を介して新しい「スレッドT1」を作成します。次に、「T1オブジェクトの同期ロック」は、同期(T1)によって取得されます。次に、t1.start()を呼び出して「スレッドT1」を開始します。
(03)「メインスレッド」はt1.wait()を実行して「T1オブジェクトのロック」をリリースし、「待機(ブロック)状態」に入ります。 T1オブジェクトのスレッドがnotify()またはnotifyall()を介して目覚めるのを待ちます。
(04)「スレッドT1」が実行された後、「現在のオブジェクトのロック」は同期(this)によって取得されます。次に、Notify()を呼び出して、「現在のオブジェクトの待機スレッド」、つまり「メインスレッド」を目覚めさせます。
(05)「スレッドT1」が完了した後、「現在のオブジェクトのロック」を解放します。その後すぐに、「メインスレッド」は「T1オブジェクトのロック」を取得し、実行します。
t1.wait()は「thread t1」を介して呼び出されるwait()メソッドですが、t1.wait()が呼ばれる場所は「メインスレッドメイン」にあります。メインスレッドは、T1.wait()を実行する前の「現在のスレッド」、つまり実行状態でなければなりません。したがって、現時点での「現在のスレッド」は「メインスレッドメイン」です!したがって、t1.wait()は、「スレッドT1」ではなく「メインスレッド」を待つようにすることです!
パッケージスレッド。test; public class notifyalltest {private static object obj = new object(); public static void main(string [] args){threada t1 = new threada( "t1"); threada t2 = new Streada( "T2"); threada t3 = new Streada( "T3"); t1.start(); t2.start(); t3.Start(); try {system.out.println(thread.currentThread()。getName()+"Sleep(3000)"); thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); } synchronized(obj){system.out.println(thread.currentthread()。getname()+"notifyall()"); obj.notifyall(); //覚醒t1.t2.t3 here}} static class threada extends thread {public threada(string name){super(name); } public void run(){synchronized(obj){try {// printout result system.out.println(thread.currentthread()。getName() + "wait"); // objオブジェクトロックobj.wait()をリリースします。 // result system.out.println(thread.currentthread()。getname() + "continue"); } catch(arturnedexception e){e.printstacktrace(); }}}}}}出力:
T1ウェイトマインスリープ(3000)T3 WAITMAIN NOTIFYALL()T2 CONTION3 CONTION1継続
(01)3スレッド「T1」、「T2」、および「T3」が作成され、メインスレッドで開始されました。
(02)メインスレッドは、睡眠中に3秒間眠ります(3000)。メインスレッドの睡眠中3秒間、3つのスレッド「T1」、「T2」、および「T3」がすべて実行されていると仮定します。例として「T1」を取ります。実行されると、obj.wait()を実行して、他のスレッドがnotify()またはnofityall()を使用して目覚めるのを待ちます。同様に、「T2」と「T3」は、他のスレッドがnofity()またはnofityall()を介してそれらを起こすのを待ちます。
(03)メインスレッドは3秒間眠り、その後実行されます。 obj.notifyall()を実行して、OBJの待機スレッドを目覚めさせます。つまり、3つのスレッド「T1」、「T2」、「T3」を覚ます。メインスレッドの同期(OBJ)が実行された直後、メインスレッドは「OBJロック」を解放します。このようにして、「T1」、「T2」、および「T3」は「OBJロック」を取得して実行し続けることができます!
Notify、NotifyAll、およびロック関係
Synchronizedのようなオブジェクトのwait()、notify()などの関数は、「オブジェクト同期ロック」で動作します。
wait()は「現在のスレッド」を待ちます。スレッドが待機状態に入るため、スレッドはロックによって保持されている「同期ロック」をリリースする必要があります。そうしないと、他のスレッドは「同期ロック」を取得できず、実行できません!
OK、スレッドがwait()を呼び出すと、ロックが保持している「同期ロック」がリリースされます。また、前の紹介によれば、notify()またはnotifyall()によって待機スレッドを覚醒させることができることがわかります。さて、質問について考えてください:待機中のスレッドの目覚めに基づいてNotify()は何ですか?または、wait()とnotify()の間の相関関係は何ですか?答えは、「オブジェクト同期ロック」に基づいています。
待機中のスレッドを目覚めさせるスレッド(「ウェイクアップスレッド」と呼ばれます)は、「このオブジェクトの同期ロック」(ここでの同期ロックは待機スレッドの同期ロックと同じ)を取得し、notify()またはnotifyAll()メソッドを呼び出す後、待機スレッドを目覚めさせることができます。ただし、待っているスレッドは目覚めます。ただし、ウェイクアップスレッドには「オブジェクトの同期ロック」がまだ保持されているため、すぐに実行できません。 「オブジェクトの同期ロック」を取得して実行し続ける前に、ウェイクアップスレッドが「オブジェクトの同期ロック」を解放するまで待つ必要があります。
要するに、notify()、wait()は、オブジェクトロックによって保持されている「同期ロック」に依存しており、各オブジェクトは1つだけです!これが、notify()、wait()などの関数がスレッドクラスではなくオブジェクトクラスで定義される理由です。
スレッド譲歩の利回り
スレッドコンセッションにより、スレッドが実行状態から準備ができた状態に変更され、同じ優先度のある他の待機スレッドが実行権を取得できるようにします。ただし、現在のスレッドがevel()を呼び出した後、同じ優先順位を持つ他のスレッドが実行権を確実に取得することは保証されていません。また、現在のスレッドが「実行中の状態」に入り、実行を継続する可能性もあります。
降伏して待ってください
(01)wait()は、スレッドが「実行状態」から「wait(blocking)状態」に入るようにすることです。
(02)wait()は、保持されるオブジェクトをスレッドリリースする同期ロックです。
(03)待機はオブジェクトの方法であり、収量はスレッドの方法です
スレッドスリープ
Sleep()の機能は、現在のスレッドを睡眠にすることです。つまり、現在のスレッドは「ランニング状態」から「睡眠(ブロッキング)状態」に入ります。 Sleep()は睡眠時間を指定し、スレッドの睡眠時間は睡眠時間よりも大きくなります。スレッドが再び目覚めると、「ブロッキング状態」から「準備ができた状態」に変わり、CPUが実行されるのを待っています。
睡眠と待機の違い
wait()の関数は、現在のスレッドが「running状態」から「wait(blocking)状態を入力し、同期ロックを解放できるようにすることです。睡眠()の関数は、現在のスレッドが「睡眠(ブロック)状態を「ランニング状態」から入力することです。 (これは実際にはそれほど違いはありません)
wait()はオブジェクトの同期ロックを解放しますが、sleep()がロックを放出しません
待機はオブジェクトの方法であり、睡眠はスレッドの方法です
参加する
メインスレッドを待ってください。そうすれば、メインスレッドが完了した後も子スレッドが実行され続けることができます。
割り込み
ブロックされたスレッドを終了するために使用されます
@OverridePublic void run(){try {while(true){//タスクを実行します...}} catch(arternedexception ie){//中断の例外のため、while(true)ループを終了し、スレッドが終了します! }}while(true)では、スレッドの割り込み()への呼び出しは、割り込みexception割り込みを生成します。中断されたキャプチャは外側(真)であるため、while(true)ループを終了します
実行状態でスレッドを終了します
@overridepublic void run(){while(!is intertrupted()){//タスクを実行...}}}スレッドを終了する一般的な方法
@OverridePublic void run(){try {//1。ISERTENRURDED()は、割り込みが真でマークされている限り、スレッドが終了することを保証します。 whip( }}
スレッドの優先順位
Javaのスレッド優先範囲の範囲は1〜10で、デフォルトの優先度は5です。「優先度の高いスレッド」は、「低優先度スレッド」の実行に先行します。 Javaには、ユーザースレッドとデーモンスレッドの2種類のスレッドがあります。それらは、ISDAEMON()メソッドによって区別できます:FALSEが返された場合、スレッドが「ユーザースレッド」であることを意味します。それ以外の場合は、「デーモンスレッド」です。ユーザースレッドは通常、ユーザーレベルのタスクを実行しますが、デーモンスレッドも「バックエンドスレッド」であり、一般的に背景タスクを実行するために使用されます。 「ユーザースレッド」が完了した後、Java仮想マシンが終了することに注意する必要があります。
各スレッドには優先事項があります。 「優先度の高いスレッド」は、「低優先度スレッド」の実行に先行します。各スレッドは、デーモンまたは非デーモンとしてマークすることができます。ランニングメインスレッドで新しい子スレッドを作成するとき、子スレッドの優先度は「作成したメインスレッドの優先度」と等しく設定され、「子スレッドはデーモンスレッドになります」ときには、「作成したメインスレッドがデーモンスレッドです」。
Java仮想マシンが開始されると、通常、1つの非デーモンスレッドがあります(このスレッドはMain()メソッドを介して開始されます)。 JVMは、次の条件のいずれかが発生するまで実行され、JVMは実行を終了します。
(01)Exit()メソッドが呼び出され、Exit()には正常に実行される許可があります。
(02)すべての「非デーモンスレッド」は死んでいます(つまり、JVMには「デーモンスレッド」のみがあります)。
デーモン
(01)メインスレッドメインはユーザースレッドで、作成する子スレッドT1もユーザースレッドです。
(02)T2はデーモンスレッドです。 「メインスレッドメイン」と「サブスレッドT1」(両方ともユーザースレッド)が実行され、デーモンスレッドT2のみが残っている場合、JVMは自動的に終了します。
プロデューサーと消費者の問題
(01)生産者は、倉庫がいっぱいでない場合にのみ生産し、倉庫がいっぱいになったときに生産を停止します。
(02)消費者は、保管製品がある場合にのみ消費でき、空の倉庫がある場合は待ちます。
(03)消費者が倉庫に消費する製品がないことを発見した場合、生産者に通知します。
(04)生産者が消耗品を生産する場合、待機中の消費者に消費するように通知する必要があります。
スレッド間の通信
方法:共有メモリとメッセージング
共有メモリ:スレッドAとスレッドB共有メモリ、スレッドA共有変数の値を更新し、メインメモリにリフレッシュし、スレッドBはスレッドAの更新された変数を読み取るためにメインメモリに移動します。通信プロセス全体がメインメモリを通過する必要があります。同期は明示的に実行されます。
変数が揮発性のタイプの場合、変数の読み取りと書き込みはアトミックになります。揮発性++と同様の複数の揮発性操作または複合操作の場合、これらの操作は原子的ではありません。
揮発性変数自体には、次の特性があります。
[可視性]:揮発性変数を読み取るときは、揮発性変数(任意のスレッド)への最後の書き込みをいつでも見ることができます。
[Atomicity]:単一の揮発性変数の読み取り/書き込みには原子性がありますが、揮発性++と同様の複合操作には原子性がありません。
揮発性書き込み:揮発性変数を書くとき、JMMはメインメモリにスレッドに対応するローカルメモリで共有変数をフラッシュします。
揮発性読み取り:揮発性変数を読み取ると、JMMはスレッドに対応するローカルメモリを無効にします。次に、スレッドはメインメモリから共有変数を読み取ります。
メッセージ配信:メッセージの送信は、メッセージが受け入れる前に暗黙的に実行されます。
threadlocal
ThreadLocalは、共有オブジェクトへのマルチスレッドアクセスの問題を解決するために使用されません。一般的に言えば、threadlocal.set()を介したスレッドへのオブジェクトは、スレッド自体で使用されるオブジェクトです。他のスレッドにアクセスする必要はなく、アクセスできません。 Threadlocalは、各スレッドが独自の独立したオブジェクトを維持できるようにします。 ThreadLocal.set()を介して実装されていませんが、各スレッドで新しいオブジェクトの操作によって作成されたオブジェクトです。各スレッドは、オブジェクトのコピーやコピーではなく、1つを作成します。新しく作成されたオブジェクトへの参照は、threadlocal.set()を介して各スレッド自身のマップに保存されます。各スレッドにはそのようなマップがあります。 threadlocal.get()が実行されると、各スレッドは独自のマップから配置されたオブジェクトを取り出します。したがって、取り出されるのは、各スレッドのオブジェクトです。 Threadlocalインスタンスは、マップキーとして使用されます。 threadlocal.set()が入力するものが複数のスレッドで共有されるオブジェクトと同じである場合、複数のスレッドのthreadlocal.get()はまだ共有オブジェクト自体を取得しますが、同時アクセスの問題がまだあります。
threadlocalの実装
java.util.collectionsをインポートします。 java.util.hashmapをインポートします。 java.util.mapをインポートします。 /** * SwreetLocalを使用したクラス * * @Author Leizhimin 2010-1-5 10:35:27 */Public Class MythReadLocal {// intまたは整数データを保存するスレッドローカル変数を定義します。 initialValue(){return 0; }}; public Integer getNextnum(){// tlの値を取得して1を追加し、t1 tl.set(tl.get() + 1)の値を更新します。 tl.getを返します(); }} class threadlocal <t> {private map <thread、t> map = collections.synchronizedMap(new hashmap <thread、t>()); public threadlocal(){}保護されたt initialValue(){return null; } public t get(){thread t = thread.currentthread(); t obj = map.get(t); if(obj == null &&!map.containskey(t)){obj = initialValue(); map.put(t、obj); } objを返します。 } public void set(t value){map.put(thread.currentthread()、value); } public void remove(){map.remove(thread.currentthread()); }}実際、Threadlocalはこれを行います。
public t get(){thread t = thread.currentthread(); ThreadLocalMap Map = getMap(t); if(map!= null){threadlocalmap.entry e = map.getentry(this); if(e!= null)return(t)e.value; } return setInitialValue(); }