Javaは、タスクを実行するスレッドを使用しています。私たちが達成同時実行であろう物事のタスクRunnableを、呼び出し可能なミッションを記述するためにもスレッドで実行方法に反映され、タスクは、タスクも実行スレッドとして記述することができ、スレッドのみキャリアタスクが、実行ユニットのタスク。
タスクと彼のドライブスレッドが同じではありませんが、それはJavaのThreadクラスに反映されますが、実際にCからのメカニズムをスレッド低レベルスレッド方式のPのJavaを制御することはできませんし、物理的に、スレッドを作成するコストかもしれこれは別の実行可能なスレッドが異なるインスタンスで実行されますので、高い、実装の観点から、そのように保存し、それらを管理する必要がある、スレッドから分離されるタスクは、理にかなっています。
これは、私たちは、スレッドプールのリソースの再利用と理解をスレッドよりよく理解するのに役立ちます。
タスク説明
タスクはRunnableを、呼び出し可能に記述することができます。呼び出し可能な主な違いは、その、そこに呼び出し可能であるのRunnableは、戻り値 ; runメソッドに加えて、実行可能なタスク本体、呼び出し可能タスク本体は、メソッド呼び出しです。
具体的にはRunnableを、呼び出し可能な説明は、参照、以下に説明します
Runnableを
TODO:コード:こちらを取り除きます
public class LiftOff implements Runnable{
protected int countDown = 10;//default
private static int taskCount = 0;
private final int id=taskCount++;//id 可以区分多个对象实例(注意final和上句static)
public LiftOff(){}
public LiftOff(int countDown){
this.countDown=countDown;
}
public String status(){
return "#"+id+"("+ (countDown>0?countDown:"LiftOff!")+").";
}
@Override
public void run() {
while(countDown-- >0){
System.out.println(status());
Thread.yield();//yield是对线程调度器的建议
}
}
}
- 注単一のスレッドは、すべてのリフトオフスレッドを作成する場合、このIDは、次いで、IDが一意であることが、複数のスレッドがリフトオフ・スレッドを作成した場合、同じIDを持つ複数のリフトオフがあってもよいです。その静的変数はスレッド安全ではありませんで!(この場合taskCount)
呼び出し可能
TODO:コード:こちらを取り除きます
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() {
return "result of TaskWithResult "+ id;
}
}
public static void main(String[] args) {
ExecutorService exec= Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<Future<String>>(); // Future
for(int i=0;i<1;i++)
results.add(exec.submit(new TaskWithResult(i))); // submit
for(Future<String> fs: results){
try {
System.out.println(fs.get());// get
}catch (InterruptedException e){
System.out.println(e);
return;
}catch (ExecutionException e){
System.out.println(e);
}finally {
exec.shutdown();
}
}
}
呼び出し可能
- 呼び出し可能な汎用タイプを受け取ることができる値の完了時に戻すことができ、リターンの所望のタイプを指定するために使用され
- 使用する必要があります
ExecutorService.submit()
呼び出し可能呼び出すために、submit()
この方法は、将来のオブジェクトを生成します - 呼び出し()メソッドを持つことができる戻り値を、あなたが宣言することができる例外がスローされます
- 呼び出し可能な汎用のインタフェースは、インタフェースに、制限があり、呼び出し可能な一般的なパラメータの種類とコール()メソッドの戻り値型と同じ
将来のインタフェース
将来の呼び出し可能インタフェースがコールの戻り値()メソッドのインターフェイスを表し、将来のためのJAVAは、へのインターフェースを提供FutureTaskの実装クラスを、実装クラスが実装するインタフェースと将来のRunnableインターフェースは、(FutureTaskはい、RunnableFutureは未来とのRunnableから継承し、RunnableFutureを実現しましたこれは、複数の継承であるが、インターフェースは、多重継承多重継承クラスではない)、実行スレッドクラスとして使用することができるFutureTaskスレッドを渡すことができます。
次の文インタフェースを使用してのメソッドと注意事項:
isDone()
将来を照会する方法が完了しているかどうか。- 呼び出し
get()
この結果を得るための方法を- 場合
isDone()
決意が完了し、呼び出しget()
、この結果を得るための方法を - 場合は
isDone()
判断が完了していない、get()
結果は準備が整うまで、それがブロックされます - **戦略:**呼び出そうとし
get()
たタイムアウトと、結果を得るために前に最初の呼び出しget(long timeout, TimeUnit unit)
、または呼び出しisDone()
タスクが完了したかどうかを確認するには
- 場合
cancel(boolean maylnterruptltRunning)
:関連呼び出し可能なタスクに未来をキャンセルしようとすると、- 通話時間の影響
- タスク場合開始する前にコールを、タスクが実行されることはありません
- タスクが場合は、すでに始まっている、
mayInterruptIfRunning
タスクを停止しようとする試みでタスクを実行するための割り込みスレッドにするかどうかを決定します。 - タスクが場合は完了した、失敗する試み、falseを返します
- 他の理由は、タスクをキャンセルしませんの場合は、falseを返します。
- メソッドの復帰後に他の方法への影響
- このメソッドが復帰した後、上の
isDone()
呼び出しは常にtrueを返します - このメソッドがtrueを返した場合、コールは
isCancelled()
常にtrueを返します
- このメソッドが復帰した後、上の
isCancelled()
:呼び出し可能なタスクが正常に完了する前にキャンセルした場合、それはtrueを返します
スレッド
限られたリソースを共有するスレッド間の文では、ロック機構は、以下の記事を参照してください。
スレッドの作成モード
実際に作成はに分けることができますスレッド従来の建物と、スレッドプールが作成され、このセクションでは、従来の方法と技術を作成した後のセクションに焦点を当てて、スレッドプールが作成されて参照してください。
- 新しいスレッド()とRunnableをを渡します
Thread t=new Thread(new LiftOFF());
t.start();
start()
この方法はすぐに返し、呼び出しをブロックしません。- 例外は、スレッド間で、すべての例外が内部ローカルタスクで生成処理する必要があり伝搬することができません
- また、あなたが渡すことができます:FutureTask(実装Runnableをします)
- 書き換えを継承したスレッドのrunメソッド
TODO:コード:こちらを取り除きます
public class SimpleThread extends Thread { // extends Thread
private int countDown = 5;
private static int threadCount = 0;
public SimpleThread() {
// Store the thread name:
super(Integer.toString(++threadCount));
start(); // start
}
public String toString() {
return "#" + getName() + "(" + countDown + "), ";
}
public void run() { // run
while(true) {
System.out.print(this);
if(--countDown == 0)
return;
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new SimpleThread();
}
} /* Output:
#1(5), #1(4), #1(3), #1(2), #1(1), #2(5), #2(4), #2(3), #2(2), #2(1), #3(5), #3(4), #3(3), #3(2), #3(1), #4(5), #4(4), #4(3), #4(2), #4(1), #5(5), #5(4), #5(3), #5(2), #5(1),
*///:~
- 変数スレッドのRunnable内に保持された自己管理Runnableを、
言葉遣いとは差をスレッドしないために、直接の後継が、我々はスレッドではないでしょうから継承された別のクラス継承のインターフェイスを実装することができます。
TODO:コード:こちらを取り除きます
public class SelfManaged implements Runnable {
private int countDown = 5;
private Thread t = new Thread(this); // new thread
public SelfManaged() { t.start(); } // start
public String toString() {
return Thread.currentThread().getName() +
"(" + countDown + "), ";
}
public void run() { // run
while(true) {
System.out.print(this);
if(--countDown == 0)
return;
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new SelfManaged();
}
} /* Output:
Thread-0(5), Thread-0(4), Thread-0(3), Thread-0(2), Thread-0(1), Thread-1(5), Thread-1(4), Thread-1(3), Thread-1(2), Thread-1(1), Thread-2(5), Thread-2(4), Thread-2(3), Thread-2(2), Thread-2(1), Thread-3(5), Thread-3(4), Thread-3(3), Thread-3(2), Thread-3(1), Thread-4(5), Thread-4(4), Thread-4(3), Thread-4(2), Thread-4(1),
*///:~
**注:他のタスクは、コンストラクタ、手段の終了前に開始することができるので、** 2及び3開始がコンストラクタで呼び出され、スレッドを開始するコンストラクタは、非常に問題になることが(タスクオブジェクトは、不安定な状態でアクセスすることができます:アイデアをプログラミングからこの文が、今TODO ???感謝していなかったもう一つのオブジェクト)理由は、
- コードビハインドクラスで雌ねじクラスを使用します
TODO:コード:こちらを取り除きます
文言のこのタイプのためにも、コンストラクタの本体内のタスクの実装上の状況に警戒しなければなりません。
// Using a named inner class:
class InnerThread1 {
private int countDown = 5;
private Inner inner;
private class Inner extends Thread { // Inner extends Thread
Inner(String name) {
super(name);
start(); // 构造器中进行start
}
public void run() {
try {
while(true) {
print(this);
if(--countDown == 0) return;
sleep(10);
}
} catch(InterruptedException e) {
print("interrupted");
}
}
public String toString() {
return getName() + ": " + countDown;
}
}
public InnerThread1(String name) {
inner = new Inner(name); // 构造内部类
}
}
// Using an anonymous inner class:
class InnerThread2 {
private int countDown = 5;
private Thread t;
public InnerThread2(String name) {
t = new Thread(name) { // InnerThread2构造器中new 了一个thread
public void run() {
try {
while(true) {
print(this);
if(--countDown == 0) return;
sleep(10);
}
} catch(InterruptedException e) {
print("sleep() interrupted");
}
}
public String toString() {
return getName() + ": " + countDown;
}
};
t.start(); // 构造器中进行start
}
}
// Using a named Runnable implementation:
class InnerRunnable1 {
private int countDown = 5;
private Inner inner;
private class Inner implements Runnable {
Thread t;
Inner(String name) {
t = new Thread(this, name);
t.start();
}
public void run() {
try {
while(true) {
print(this);
if(--countDown == 0) return;
TimeUnit.MILLISECONDS.sleep(10);
}
} catch(InterruptedException e) {
print("sleep() interrupted");
}
}
public String toString() {
return t.getName() + ": " + countDown;
}
}
public InnerRunnable1(String name) {
inner = new Inner(name);
}
}
// Using an anonymous Runnable implementation:
class InnerRunnable2 {
private int countDown = 5;
private Thread t;
public InnerRunnable2(String name) {
t = new Thread(new Runnable() {
public void run() {
try {
while(true) {
print(this);
if(--countDown == 0) return;
TimeUnit.MILLISECONDS.sleep(10);
}
} catch(InterruptedException e) {
print("sleep() interrupted");
}
}
public String toString() {
return Thread.currentThread().getName() +
": " + countDown;
}
}, name);
t.start();
}
}
ライフサイクルスレッド
(1を参照するために参照するテキストの最後を参照してください)
(5つの状態にスレッドモデル論じ)スレッドの切り替え状態。
-
新しい状態:プログラムが使用して新しいをのみ、そのJVMによって、あなたがキーワードのスレッドを作成した後、スレッドが新しい状態になっているこの時間を割り当て、メモリ、および初期化するために、そのメンバ変数の値を
-
レディ状態:スレッドオブジェクトを呼び出すようにするときの開始を()メソッドの後、スレッドがレディ状態です。作成するには、Java仮想マシン呼び出しスタックとプログラムカウンタ方法を実行するようにスケジュールされるのを待って、
-
**動作状態:スレッドがCPU、開始取得する準備ができている場合は、実行メソッドの実行()**スレッドを、スレッドが実行されています
-
**ブロッキング状態:**占有リソースを実行中のスレッドを失った後、ブロッキング状態(実行)を入力します
TODO:新しいコールが矢印に新しい状態を追加する必要があります
TODO:これは上下二つのマップに入れなければなりません
新しいスレッドと準備の注意事項
- サブスレッドが実行を開始した直後のスレッド()メソッドを呼び出して開始する、プログラムを使用することができ
Thread.sleep(1)
、それは1ミリ秒内でアイドルCPUないので、それは、睡眠1ミリ秒、1ミリ秒は十分にある現在実行中のスレッド(メインスレッド)を作成します私たちは、準備完了状態で別のスレッドを実行するために行きますので、あなたは、子スレッドがすぐに実行を開始することができます。 - runメソッドとstartメソッド:スタートスレッドが(スタート)メソッドではなく、run()メソッド。スレッドオブジェクトのrun()メソッドを呼び出すことはありません。しかし、実行中(が、オブジェクト()メソッドによって直接実行呼び出したスレッドは、run()メソッドは、即座に実行される場合、コールSTART0方法スレッドを開始するには、システムは、ハンドルへの実行のスレッドとして()メソッドを実行します)メソッドが戻る前に、他のスレッドが同時に実行することができません。換言すれば、システムスレッドオブジェクト通常のオブジェクトとして、およびrun()メソッドは、一般的な方法ではなく、実行のスレッドです。スレッドのrun()メソッドを呼び出した後、スレッドは、もはや新しい状態である再スタート()メソッドのスレッドオブジェクトを呼び出さないことに留意すべきではありません。新しい状態方法でスレッドにのみコールスタートは()、それ以外の場合はIllegaIThreadStateException例外につながります。
ブロックされた状態にケースのスレッド
- スレッドの呼び出し睡眠()メソッドはあきらめリソースがスリープ状態にタスクによって占めプロセッサを、この場合には、タスクは、指定された時間内で実行されることはありません。(ブロック解除:睡眠()メソッド指定された経過時間)
- スレッドは、コールブロッキングIO方法において、いくつかの入力及び出力待ち完了(:ブロッキングIO方法戻ったブロック解除)は、メソッドが戻る前に、スレッドがブロックされています
- タスクがしようとしているオブジェクト上でその同期制御メソッドを呼び出しますが、オブジェクトのロックが使用できない別のタスクが、ロックを取得してきたので。そのスレッドがされて取得しようとして同期モニタ(ロック)が、同期モニターは、別のスレッドによって保持されています。シンクモニタのサポート技術情報(ロック)、後者は、より詳細な紹介となります(閉塞を持ち上げる:取得に成功した同期のモニタを取得しようとしています)
- 呼び出すことによって)(待機するスレッドを中断し、スレッドが待っているの通知(通知または信号)(ブロック解除:待っている他のスレッドによって発行された通知)
- プログラムは、スレッドの呼び出しを中断()スレッドのメソッドが中断されています。しかし、この方法では、につながることは容易であるデッドロックあなたは、このメソッドを使用して回避しようとする必要がありますので、。(ブロックを解除:コール
resdme()
回復方法)
スレッドの終了のお知らせ
- 例外エラーまたはスレッドがキャッチされないがスローされます
- スレッドは直接スレッド終了するstop()メソッドを呼び出します - 簡単にデッドロックにつながり、通常はお勧めできません
その他のライフサイクルの考慮事項
- yield文(つまり
Thread.yield()
):スレッドが最も重要な部分のライフサイクルを完了したことを示し、それは今の時間の別のタスクの実行期間にスイッチへの好機です。これは、完全に任意であり、それが発生したり発生しないことがあります。 - 態勢の動作状態との間の変換とは、通常、プログラム制御の対象ではない、しかしによって、システムのスレッドスケジューリング決定。
- するためには、テストスレッドがすでに死んで、あなたがスレッドオブジェクトの呼び出すことができます
isAlive()
スレッドがある場合は、この方法を準備遮断する、実行している、とき三つの状態、メソッドが返すtrueに、スレッドがであるときに新しい死、メソッドが返す状態偽は - 死は死である、それを再起動するスレッド死んだ()メソッドの開始を呼び出そうとしないで、スレッドがスレッドとして再び使用することはできません。例外がスローされますIllegaIThreadStateException
- 睡眠の文:
一般的なスレッド処理
優先順位
使用することができますgetPriority()
し、setPriority()
読むために、スレッドの優先度設定(の優先順位run()
のタスクがまだ開始されていないので、セットの先頭には、コンストラクタで何の利益にそれを設定していない。TODO:???まだこの点を鑑賞します) 。
唯一MAX_PRIORITY、NORM_PRIORITYとMIN_PRIORITY三つのレベルを使用する場合に優先順位を調整する場合、スレッドの優先度の移植を行います。
歩留まりの譲歩
歩留まりを使用すると、スケジューラは、他のスレッドがCPUを使用できますが、彼が採用されることを確保するための機構がない、提案収率は、他のスレッドの同じ優先度が重大な制御のために実行したり、アプリケーションを調整することができているが推奨されています私たちは、収量に依存しません。
yield文(つまりThread.yield()
):スレッドが最も重要な部分のライフサイクルを完了したことを示し、それは今の時間の別のタスクの実行期間にスイッチへの好機です。これは、完全に任意であり、それが発生したり発生しないことがあります。
バックグラウンドスレッド
-
これは、非メインバックグラウンドスレッドです。
-
スレッドの開始前に呼び出されなければならない
setDaemon(boolean on)
、それがバックグラウンド・プロセスになる設定する方法。例としては、次のとおりです:Thread daemon = new Thread(new Runnable() {...}); daemon.setDaemon(true); daemon.start();
-
任意のスレッドが作成されたバックグラウンドスレッドを自動的にバックグラウンドスレッドに設定します。
-
バックグラウンドスレッドは、他のすべての非バックグラウンドスレッドの終了時に終了しますfinally文を実行しない場合があります。
finally文の最終地点ための指示とは限りません。
TODO:コード:こちらを取り除きます
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Thread daemon = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true){
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread()+" "+this);
}
}catch (InterruptedException e){
System.out.println("sleep() interruted");
}finally {
System.out.println("all down");
}
}
});
daemon.setDaemon(true);
daemon.start();
}
System.out.println("All started");
TimeUnit.MILLISECONDS.sleep(175);
}
// output
All started
Thread[Thread-7,5,main] A$1@42337127
Thread[Thread-9,5,main] A$1@fb49a4f
Thread[Thread-8,5,main] A$1@47c48106
Thread[Thread-6,5,main] A$1@6fada00f
Thread[Thread-0,5,main] A$1@3cbeafaf
Thread[Thread-2,5,main] A$1@22e90474
Thread[Thread-1,5,main] A$1@3d142f7d
Thread[Thread-3,5,main] A$1@56a590e
Thread[Thread-4,5,main] A$1@1eec3130
Thread[Thread-5,5,main] A$1@2e24d89c
観察出力が見つかりません、そして印刷するバックグラウンドスレッドのループ、それ自体を行わずに、バックグラウンドスレッドが最後に実行メソッドを終了するので、印刷は、すべてのダウンに最後に行われなかったことができます
この動作は、正確である以前に約束した場合でも、最終的に得られるベースすると、この動作を望んでいないが、状況はそう残ります。最後の非バックグラウンドスレッドが終了するときに、バックグラウンドスレッドでは、「急に」終了します。そのため、メイン終了後、JVMは、あなたが表示したいことの確認のいずれかの形式ずに、すぐにすべてのバックグラウンド・プロセスを終了します。あなたがバックグラウンドスレッドをオフにするエレガントな方法することができないので、ので、彼らは良いアイデアはほとんどありません。すべてのタスクExecutorコントロールが同時にオフすることができるので、非背景キュータは、通常、より良い方法です。あなたはこの章の後半に表示されるように。この場合、実行は通常の方法でシャットダウンされます。
スレッドに参加
スレッドが別のスレッドにトンを呼び出す場合t.join()
、スレッドこの呼び出しはトンが復元されたターゲットスレッドの終了まで中断されます。あなたもすることができt.join()
、この期間の終わりにターゲットスレッドが、期限が切れていなかったそう場合は、必ず戻ります方法に参加、あなたが呼び出しタイムアウトパラメータを追加します。
中断することができる方法を結合するために呼び出して、実際には、呼び出し元のスレッドに割り込みメソッドを呼び出すことです。
スレッドで例外をキャッチ
糸の特性の性質により、私たちは、キャプチャスレッドの例外から逃れることができません。それはできThread.UncaughtExceptionHandler
例外処理、各スレッドオブジェクトの添付ファイルを許可する例外ハンドラのためになります。
戦略:あなたはどこにでも例外ハンドラを同じコードを使用することが判明している場合は、より簡単な方法は、このプロセッサのデフォルトのキャッチされない例外ハンドラとしてThreadクラスとセットで静的フィールドを設定することです。それは存在しない唯一のスレッド固有でプロセッサのキャッチされない例外ハンドラの場合に呼び出されます。システムチェックは、スレッド固有のバージョン、見つからない場合は、スレッドグループは、独自の持っているかどうかをチェックするuncaughtException()
一切場合、呼び出していない、方法をdefaultUncaughtExceptionHandler
。例として:
public class SettingDefaultHandler {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
その他の重要なプロセス
以下は、処理の長さが長すぎる説明し、このセクションでは、持っているセクションを参照してください
- ローカルストレージのThreadLocal
- タスク/スレッドの終了
- スレッド間のコラボレーション
スレッドについての留意事項
それは、スレッドセーフであるかどうか
- 静的変数のスレッドセーフ、上述した実施のRunnableの下のメモを参照してください
- print文スレッドセーフ
参照
- Javaのマルチスレッドの学習(C)---スレッドのライフサイクル
- 章の逆数第4版、Javaプログラミングのアイデア中国語版