同時通信のスレッド間のJava学習記録
揮発性およびsynchronizedキーワード
待機中/通知メカニズム
パイプライン入力/出力ストリーム
供給ライン入力/出力ストリームと出力ストリームパイプライン入力/出力ストリームことを除いて、通常のファイル/ネットワーク入力または出力ストリームは、主にスレッド間のデータ伝送に使用される
送信用記憶媒体。
パイプライン入力/出力ストリームを実装するには、次の2種類が挙げられます。
- バイト指向:PipedOutputStreamの、持つPipedInputStream
- 文字指向:PipedWriter、PipedReader
package com.littlefxc.examples.base.thread.pipe;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
/**
* 管道输入/输出主要用于线程间的数据传输,传输的媒介是内存。具体实现:
* <br>面向字节:
* <ul>
* <li>PipedWriter</li>
* <li>PipedReader</li>
* </ul>
* <br>面向字符:
* <ul>
* <li>PipedOutputStream</li>
* <li>PipedInputStream</li>
* </ul>
*
* @author fengxuechao
* @date 2019/2/26
**/
public class Piped {
public static void main(String[] args) throws IOException {
PipedWriter writer = new PipedWriter();
PipedReader reader = new PipedReader();
// 将输出流和输入流进行必要的连接
writer.connect(reader);
Thread printThread = new Thread(new Print(reader), "PrintThread");
printThread.start();
int receive = 0;
try {
while ((receive = System.in.read()) != -1) {
writer.write(receive);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
writer.close();
}
}
static class Print implements Runnable {
private PipedReader reader;
public Print(PipedReader reader) {
this.reader = reader;
}
@Override
public void run() {
int receive = 0;
try {
while ((receive = reader.read()) != -1) {
System.out.print((char) receive);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
例を実行し、入力した文字列、printThreadして出力を見ることができるように。
ヒント:
フローのパイプによる種類について、すなわち、呼び出し、結合である必要がありconnect()
、それ以外の場合は例外がスローされます。
Thread.join()を使用
多くの場合、メインスレッドは、時間のかかる操作の多くの子スレッド場合は、子スレッドを作成して開始し
、子スレッドが終了することが多いの前にメインスレッドを、しかし、メインスレッドは、他の事項と必要性を取り引きを終えた場合サブスレッドの結果に対処するためには、
メインスレッドは、我々は()メソッドを持って参加し、この時間を使用する必要があり、完成されたサブスレッドの実行が終了した後、長く待つ必要があります。また、
スレッドはまた、join()メソッドを使用する必要が別のスレッドを待つ必要があります。
提供するためのThreadクラスの追加join()
方法は、だけでなく、提供join(long millis)
、join(long millis, int nanos)
二つの方法がタイムアウト機能を持っています。これらの2つの方法のタイムアウトは、スレッドのスレッドが指定されたタイムアウト期間内に終了しない場合は、と言った
ことは、タイムアウトメソッドから返されます。
焦点を合わせ一見join(long millis)
例:
package com.littlefxc.examples.base.thread;
/**
* @author fengxuechao
*/
public class JoinLongTest {
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join(1000);// 主线成等待子线程1秒
// Thread.sleep(1000);
System.out.println("主线程结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static public class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("子线程结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
結果:
ThreadLocalの
我々は、スレッドのリソースは安全性の問題を確保するときの話をする前に、使用同期ロックは、スレッドの安全性を確保することを意味します。もう一つの方法は、することです孤立リソースの練習を。
いわゆるアイソレーション各スレッドは、独自のローカルリソースを使用していること。、アクセスする他のスレッドを防ぐために、リソースの分離
、単離された資源の観点から、そののみ、現在のスレッドにアクセスする能力。現在のスレッドが唯一のデータにアクセスすることができますので、もちろん、それはスレッドセーフです。
典型的な例ですServlet
。
単純にThreadLocalを使用
まずは、徐々にそれらを変換し、その後、ThreadLocalのクラスを使用しないようにしましょう。
package com.littlefxc.examples.base.thread.threadlocal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author fengxuechao
* @date 2019/2/26
**/
public class ThreadLocal1 {
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
es.execute(new ParseDate(i));
}
es.shutdown();
}
private static class ParseDate implements Runnable {
int i = 0;
public ParseDate(int i) {
this.i = i;
}
@Override
public void run() {
try {
ParseDateWithSync
Date date = SDF.parse("2019-02-26 16:23:" + i % 60);
System.out.println(i + ":" + date);
// }
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
結果:
一般に、この問題は、マルチスレッド環境でのSimpleDateFormatのインスタンスを共有するので、もし、スレッドセーフで、マルチスレッド環境でのSimpleDateFormatが主な原因である
ので、あなたが同様の日付でグローバルクラスのSimpleDateFormatオブジェクトを定義など子供は確かに上記のエラーが表示されます。
一つの解決策は、コメントを削除した後に再実行することができ、それはこの問題は表示されません上記のコードでロックすることです。
しかし、今、私は上記のコードのために変更を加える、各スレッドのSimpleDateFormatオブジェクトを保持するスレッドローカル変数を保持しているのThreadLocalオブジェクトを使用します:
package com.littlefxc.examples.base.thread.threadlocal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author fengxuechao
* @date 2019/2/26
**/
public class ParseDateWithThreadLocal {
static final String pattern = "yyyy-MM-dd HH:mm:ss";
private static final ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>();
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
es.execute(new ParseDate(i));
}
es.shutdown();
}
private static class ParseDate implements Runnable {
int i = 0;
public ParseDate(int i) {
this.i = i;
}
@Override
public void run() {
try {
// 如果当前线程不持有 SimpleDateFormat 对象。那就新建并保存设置在当前线程中,如果已持有,则直接使用。
if (threadLocal.get()==null) {
threadLocal.set(new SimpleDateFormat(pattern));
}
Date date = threadLocal.get().parse("2019-02-26 16:23:" + i % 60);
System.out.println(i + ":" + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
注意:また、上記のコードから見ることができ、各スレッドの仕事のためにオブジェクトを代入するのThreadLocalで行われ、されていない
が、アプリケーションレベルを確実にするために、開発者が必要です。ThreadLocalのはコンテナの役割を果たしました。