これまでのところ、あなたはすべてのシーケンシャルプログラミングを学んできた、シーケンシャルプログラミングの概念は、時間の実行、シーケンシャルプログラミングコースで1つのタスクのみが、多くの問題を解決することができますが、特定のタスクのために、我々は重要なコンカレント・プログラムを実行することができる場合でありますその一部は重要ですが、また非常にあなたを持って、プログラムの効率を向上させる同時の利便性を享受することができます。しかし、並行プログラミングの理論と技術の習熟度、あなたのためのCRUDますが、オブジェクト指向の一種であり、あなただけの飛躍の同じ種類を学びます。
あなたが見ることができるように並列タスクが互いに干渉したときに、実際の並行性の問題が続いて起こります。あなたは同時実行の問題を無視した場合、あなたは最終的にそれを負担します:そして、同時実行の問題は、障害がめったに起こらないされているので、あなたは私たちが彼らの条件を検討する必要がある理由である、実際のテストプロセスでそれらを無視する傾向が再現するのは難しいことではありませんあなたに害をもたらすために。
同時多面的な
より速く実行
各スライスは別々のプロセッサ上で実行されている、あなたがプログラムを高速に実行したい場合はスピードの問題は、簡単に聞こえるので、スライスにカットすることができます。これらのタスクは、それぞれ他のではないことを前提連絡先。
注:マルチコアチップの代わりに出現の形態のプロセッサの速度を向上させます。
あなたはマルチプロセッサマシンを使用している場合は、大幅にスループットが向上し、プロセッサ間で複数のタスクを配布することができます。しかし、複雑な手順は、一般に、単一のプロセッサのパフォーマンスを向上させることができます。増加多くの大型のマルチプロセッサの性能上の単一の処理オーバーヘッドのパフォーマンスオーバーヘッド线程切换
(別のスレッドへのスレッドの切り替え)のための重要な基礎。表面には、単一のタスクとして、プログラムのすべての部分は、時間のスレッド切り替えを節約、少し小さいコストを実行しているようです。
改善された設計コード
これらのスレッドが同時に動作するように、いつでも、単一CPUマシンプログラム上でマルチタスクの使用が唯一の仕事の実装にまだある、あなたは肉眼に出力をコンソール、これは何もなく、カバーアップされていないCPUを埋め、各タスクのCPUこれは、決まった時間スライスを提供していません。Javaのスレッドメカニズムを使用すると、スレッドが他のスレッドに、スイッチを切り替えることができます譲歩文のいくつかの種類を記述する必要があることを意味し、先制です。
基本的なスレッド機構
並行プログラミング私たちのプログラムが実行している別の、独立したタスクの数に分けることができるように。マルチスレッド・メカニズムを使用することにより、各タスクによって、これらの独立したタスクを执行线程
駆動します。スレッドは、単一の順次処理の制御フローです。このように、単一のプロセスは、複数のタスクの同時実行を持つことができますが、あなたのプログラムは、各タスクは独自のCPUと同じを持って見えます。セグメンテーションは、基礎となるCPU時間は、通常、あなたがそれを考慮する必要はありません。
カスタムタスク
スレッドは、タスクを駆動することができますので、あなたが行うことができるタスク、記述するための方法が必要ですRunnable
だけでRunnableを実装して、タスクを定義するために、インターフェイスを提供するをrun
処理するためのロジックを実現します。
public class TestThread implements Runnable{
public static int i = 0;
@Override
public void run() {
System.out.println("start thread..." + i);
i++;
System.out.println("end thread ..." + i);
}
public static void main(String[] args) {
for(int i = 0;i < 5;i++){
TestThread testThread = new TestThread();
testThread.run();
}
}
}
タスクの実行方法は、もはや必要になるまで永遠にタスクの実行を作り、リサイクルのいくつかのフォームを持っていますので、条件のうちの実行を設定する方法は、(私たちは以下について説明します、ランから直接返すためのオプションがあります。)
静的メソッドを使用して実行でThread.yield()
あなたがスレッドのスケジューリングを使用することができ、それがスイッチする提案のスレッド機構を意味:あなたは、実装の重要な部分をやったし、他のスレッドへの残りは実行を実行します。むしろ執行よりも、お薦めの実装に注意してください。あなたが興味深い出力が表示されます)(下のThread.yieldを追加
public void run() {
System.out.println("start thread..." + i);
i++;
Thread.yield();
System.out.println("end thread ..." + i);
}
Threadクラス
Runnableをが道を変革伝統的な方法は彼をホスティングThreadクラスを使用することで、スレッドを実装するためにThreadクラスを用いて、以下に示されています。
public static void main(String[] args) {
for(int i = 0;i < 5;i++){
Thread t = new Thread(new TestThread());
t.start();
}
System.out.println("Waiting thread ...");
}
スレッドのコンストラクタは、このスレッドでタスクを開始するためにRunnableのrunメソッドを呼び出して、必要な初期化を実行するスレッドのオブジェクトの開始スレッド()メソッドを呼び出し、唯一のRunnableオブジェクトを必要とし、。runメソッドがまだ終わっていません前に、それが返された実行見ることができます。この方法は、それが次のコマンドを実行します終了するまで他の言葉では、プログラムが実行されません。
runメソッド内の各スレッドの名前をプリントアウトし、糸の異なるスイッチングおよびスケジューリングを参照することをお勧めします
@Override
public void run() {
System.out.println(Thread.currentThread() + "start thread..." + i);
i++;
System.out.println(Thread.currentThread() + "end thread ..." + i);
}
このスレッドの切り替えやスケジューリングが引き渡され线程调度器
、あなたのマシン上で複数のプロセッサがある場合、自動制御に、スレッドスケジューラは、静かに、プロセッサ間でスレッドを配布します。スレッドスケジューリングメカニズムが未定であるため、すべてのオペレーティング結果は、同じではありません。
エグゼキュータを使用してください
CachedThreadPool
JDK1.5はjava.util.concurrentののパッケージエグゼキュータキュータは、あなたは並行プログラミングを簡素化するThreadオブジェクトを管理します。タスクは、中間オブジェクトとして実行され、クライアントが直接別のタスクを実行し、クライアントのタスクとの間のエグゼキュータは、間接のレベルを提供します。エグゼキュータは、スレッドのライフサイクル管理を表示することなく、あなたが実行する非同期タスクを管理することができます。
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for(int i = 0;i < 5;i++){
service.execute(new TestThread());
}
service.shutdown();
}
私たちは、Threadオブジェクトを作成するために、ディスプレイを交換するエグゼキュータを使用しています。CachedThreadPool
タスクごとにスレッドを作成します。注:ExecutorServiceのオブジェクトは静的で使用することでExecutors
作成され、この方法は、執行の種類を決定することができます。上のshutDown
ExecutorServiceのに提示新しいタスクを防ぐことができます呼び出し、このスレッドは、エグゼキュータ内のすべてのタスクが完了した後に終了します。
FixedThreadPool
FixedThreadPoolは、あなたがマルチスレッドを開始するスレッドの限られたセットを使用することができます
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(5);
for(int i = 0;i < 5;i++){
service.execute(new TestThread());
}
service.shutdown();
}
FixedThreadPoolを使用すると、1回実行前の高いスレッド割り当てを行うことができ、したがって、スレッドの数を制限することができます。あなたは、各タスクにスレッドを作成するための一定のオーバーヘッドを支払う必要はありませんので、これは、時間を節約できます。
SingleThreadExecutor
SingleThreadExecutorはFixedThreadPool 1のスレッドの数です、そしてあなたがSingleThreadPoolに複数回のタスクを提出する場合は、これらのタスクは、次のタスクの開始前に終了する各ミッションは、すべてのタスクが同じスレッドを使用しますラインアップします。SingleThreadPoolはすべての彼の仕事に提出シリアライズし、独自の(隠された)停止キューを維持します。
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for(int i = 0;i < 5;i++){
service.execute(new TestThread());
}
service.shutdown();
}
あなたは、タスクが実行する隣で、結果出力から見ることができます。私は5つのスレッドのタスクを割り当てられていたが、これらの5つのスレッドは、我々は以前にそれがスレッドは、その後、残りのスレッドは「飲み続けることを彼の実行まで、毎回効果をオフにスワップアウトする必要が見たようにされていませんこのスレッドの「実行パス。あなたはいつでも一つだけ実行中のタスクがあることを確認するためにSingleThreadExecutorを使用することができます。
戻り値は、タスクから生成されます
Runnableを、タスクの独立した実装ですが、それは任意の値を返しません。あなたは、タスクが完了した時点で値を返したい場合は、あなたが使用することを検討する必要があり、この時Callable
JDK1.5以降に導入されたインタフェースは、その呼び出すことによってsubmit
メソッドを、その戻り値は、Futureオブジェクトの上に配置することができ、その後、 ()メソッドは、対応するGETに応じて提出し得た後に値を返します。
public class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id){
this.id = id;
}
@Override
public String call() throws Exception {
return "result of TaskWithResult " + id;
}
}
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executors = Executors.newCachedThreadPool();
ArrayList<Future<String>> future = new ArrayList<>();
for(int i = 0;i < 10;i++){
// 返回的是调用 call 方法的结果
future.add(executors.submit(new TaskWithResult(i)));
}
for(Future<String> fs : future){
System.out.println(fs.get());
}
}
}
提出()メソッドは、オブジェクトの未来を返し、今後のオブジェクトストアは、あなたのリターンの結果です。また、使用することができますisDone
将来が完了したかどうかを照会します。
休眠
タスクの動作に影響する簡単な方法は、選択された睡眠時間を考えると、スレッドの睡眠をすることです、その呼び出しsleep()
方法を、一般的に使用されTimeUnit
た時間のクラス置き換えるためにThread.sleep()
、次のような方法を、例を示します。
public class SuperclassThread extends TestThread{
@Override
public void run() {
System.out.println(Thread.currentThread() + "starting ..." );
try {
for(int i = 0;i < 5;i++){
if(i == 3){
System.out.println(Thread.currentThread() + "sleeping ...");
TimeUnit.MILLISECONDS.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "wakeup and end ...");
}
public static void main(String[] args) {
ExecutorService executors = Executors.newCachedThreadPool();
for(int i = 0;i < 5;i++){
executors.execute(new SuperclassThread());
}
executors.shutdown();
}
}
睡眠中TimeUnitでのメソッドの比較()メソッドとのThread.sleep()、以下、このブログを参照してください。
(https://www.cnblogs.com/xiadongqing/p/9925567.html)
優先順位
実行の各スレッドについて、上記のスレッドスケジューラは予測不可能であるし、優先度のタスクがそれを強制したいですランダム実行スレッドスケジューラを伝える方法がありませんか?ただし、スレッドの実行優先度が比較的高く、優先度の高いスレッドの実行優先順位を許可するように傾斜したスレッドスケジューラ「ライダーはすぐにシングルを送って与えてください」スレッドスケジューラを伝える、スレッドの優先順位のステータスを設定することができますこれは、低優先度のスレッドが実行できないという意味ではありません、つまり、優先順位は、デッドロックにはなりません。低い優先順位のスレッドが単に低い周波数を実行します。
public class SimplePriorities implements Runnable{
private int priority;
public SimplePriorities(int priority) {
this.priority = priority;
}
@Override
public void run() {
Thread.currentThread().setPriority(priority);
for(int i = 0;i < 100;i++){
System.out.println(this);
if(i % 10 == 0){
Thread.yield();
}
}
}
@Override
public String toString() {
return Thread.currentThread() + " " + priority;
}
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for(int i = 0;i < 5;i++){
service.execute(new SimplePriorities(Thread.MAX_PRIORITY));
}
service.execute(new SimplePriorities(Thread.MIN_PRIORITY));
}
}
toString()メソッドは、そう使用して、被覆されているThread.toString()
スレッドの印刷方法名。あなたは、デフォルトの出力スレッドを上書きすることができ、そこに採用されているスレッド[プール-1-スレッド 1,10、メイン] 出力のこの形。
出力することで、あなたは、最後の最も優先度の高いスレッドの残りの最も低い優先度のスレッドを見ることができます。今回スレッドがタスクを実行していないため、優先度が、彼らはどんな良いものではありませんコンストラクタで設定され、実行の開始時に設定されていることに注意してください。
10優先JDKが、一般的にのみ存在するがMAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三段階。
譲歩を作ります
私は最も重要な部分でタスクを完了して、他のスレッドを与えることがあります。私たちは、あなたが、ほぼ実行するためのrun()メソッドでは、すでにスレッドを知っていれば、それはスレッドスケジューラへのヒントであってもよいし、上記CPU。このヒントは、収率()メソッドによって行われます。
非常に重要なポイントではなく、CPUのスイッチを強制するよりも、ハンドオーバCPUを実行することをお勧めします()、Thread.yieldです。
アプリケーションを呼び出すときに重大な制御のためか、に依存することはできませんyield()
実際には、歩留まり()メソッドは、多くの場合、虐待を受けている、方法。
バックグラウンドスレッド
バックグラウンド(デーモン)スレッド、スレッドは、このスレッドに属している必要はありません提供バックグラウンドで実行されているサービスを指します。すべての非バックグラウンドスレッドの終了時に、プログラムが停止します、そして、それはすべてのバックグラウンドスレッドを終了します。逆に、限り、まだ実行されている任意の非バックグラウンドスレッドがあるので、プログラムは終了されることはありません。
public class SimpleDaemons implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() + " " + this);
} catch (InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0;i < 10;i++){
Thread daemon = new Thread(new SimpleDaemons());
daemon.setDaemon(true);
daemon.start();
}
System.out.println("All Daemons started");
TimeUnit.MILLISECONDS.sleep(175);
}
}
これは、各サイクル10個のスレッドで作成され、各スレッドは、バックグラウンドスレッドに設定され、かつ循環が、その後しばらくの間、スリープ状態にメインスレッドの後に実行を停止し、その後、10時間、および出力情報となりますため、その後、実行し始めました。各実行サイクルでは、それが現在のスレッドに関する情報を出力します、メインスレッドの実行が終了している、プログラムの実行が完了しています。そのためdaemon
、バックグラウンドスレッドは、メインスレッドの実行に影響を与えることはできません。
ときあなたが取るしかし、daemon.setDaemon(true)
無限ループになります(真)ながら、削除するための時間を、それが最も重要なタスクを実行するには、メインスレッドとなっている、サイクルが続く停止することができなかったでしょう。
ThreadFactory
オブジェクトのスレッドを作成する必要がありました。あるいはハードワイヤードまたは使用のRunnableインタフェーススレッドスレッド工場は、プログラムは、特別なスレッドサブクラスと優先順位を使用することを可能にします。一般的な方法を作成します。
class SimpleThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
Executors.defaultThreadFactoryが作成される既知の値に戻る前に、それを設定し、より便利なスレッドコンテキストを達成するための簡単な方法を提供し、
ThreadFactoryは、唯一の方法は、スレッドのメソッドを作成することであるインタフェースであり、
public interface ThreadFactory {
// 构建一个新的线程。实现类可能初始化优先级,名称,后台线程状态和 线程组等
Thread newThread(Runnable r);
}
のは、ケースを見てみましょうThreadFactory
public class DaemonThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
}
public class DaemonFromFactory implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() + " " + this);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool(new DaemonThreadFactory());
for(int i = 0;i < 10;i++){
service.execute(new DaemonFromFactory());
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(500);
}
}
Executors.newCachedThreadPool
私たちは、必要に応じて新しいスレッドを作成するスレッドプールを作成し、スレッドプールオブジェクトを受け入れることができ、それらには以前に構築スレッドを再利用するために利用できるようになりますし、必要に応じて、新しいスレッドを作成するThreadFactoryを使用しています。
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
スレッドに参加
スレッドが他のスレッドの上で呼び出すことができるjoin()
方法、効果は通常を実行するために、第2のスレッドが終了するまである程度の時間を待つことです。スレッドが別のスレッドtの上t.join()メソッドを呼び出した場合、このスレッドは、返信トンターゲットスレッドの終了まで中断されます(あなたがt.isAliveを(使用することができます)真または偽の判断を返します)。
有効期限、有効期限、自動的に戻りますが、参加方法を設定するには、timeoutパラメータを呼び出すときにも持ち込むことができます参加します。
また、スレッド上で呼び出すために練習を中断することができます参加するに呼び出してinterrupted
やってみ使用する必要があり、その後、方法... catch節
public class TestJoinMethod extends Thread{
@Override
public void run() {
for(int i = 0;i < 5;i++){
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted sleep");
}
System.out.println(Thread.currentThread() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoinMethod join1 = new TestJoinMethod();
TestJoinMethod join2 = new TestJoinMethod();
TestJoinMethod join3 = new TestJoinMethod();
join1.start();
// join1.join();
join2.start();
join3.start();
}
}
join()メソッドは、死ぬためのスレッドを待機します。言い換えれば、それはそのタスクを完了するために、スレッドに参加するまで実行を停止し、現在実行中のスレッドの原因となります。
スレッドの例外がキャッチ
糸の性質上、あなたがキャプチャ異常から逃れることができないスレッドに、異常は、このエラー例外をキャッチするために特別な手順を実行しない限り、それはJava5で、コンソールに広がるだろう、一度実行方法タスクをエスケープあなたは、スレッドによってグループをキャプチャすることができますが、Java5の後、あなたはスレッドグループが良い試みではないので、問題を解決するためにエグゼキュータを使用する必要があります前に。
次の作業はrunメソッドの実行中にスローされます例外であり、この例外がrunメソッドの外にスローされます、そしてメインの方法は、それをキャプチャすることはできません
public class ExceptionThread implements Runnable{
@Override
public void run() {
throw new RuntimeException();
}
public static void main(String[] args) {
try {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new ExceptionThread());
}catch (Exception e){
System.out.println("eeeee");
}
}
}
この問題を解決するために、我々はJava5は新しいインターフェイスを提供、エグゼキュータは、スレッドの道を生成します修正する必要がありThread.UncaughtExceptionHandler
ますが、各スレッドに例外ハンドラを添付できます。Thread.UncaughtExceptionHandler.uncaughtException()
キャッチされなかっによる死は、スレッドと呼ばれているときに近いです。
public class ExceptionThread2 implements Runnable{
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run() by " + t);
System.out.println("eh = " + t.getUncaughtExceptionHandler());
// 手动抛出异常
throw new RuntimeException();
}
}
// 实现Thread.UncaughtExceptionHandler 接口,创建异常处理器
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
public class HandlerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println(this + " creating new Thread");
Thread t = new Thread(r);
System.out.println("created " + t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("ex = " + t.getUncaughtExceptionHandler());
return t;
}
}
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool(new HandlerThreadFactory());
service.execute(new ExceptionThread2());
}
}
プログラムの中で、この工場で作成されたスレッドを検証するための追加のトラッキングメカニズムを追加するために渡されますUncaughtExceptionHandler
、あなたがキャッチされない例外を通して見ることができますuncaughtException
キャプチャします。
出典:
「Javaプログラミングのアイデア」
https://www.javatpoint.com/join()-method