Java多线程之通过管道线程间通信(字节流、字符流),类ThreadLocal与类InheritableThreadLocal的使用
在Java语言中提供了各种各样的输人/输出流Stream,使我们能够很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输人管道中读数据。通过使用管道,实现不同线程间的通信,而无须借助于类似临时文件之类的东西。
在Java的JDK中提供了4个类来使线程间可以进行通信:
1)PipedInputStream和PipedOutputStream
2)PipedReader和PipedWriter
1、通过管道进行线程间通信:字节流/字符流
public class WriteData {
public void writeMethod(PipedOutputStream out) { //字符流这里换成PipeWriter
try {
System.out.println("write :");
for (int i=0; i<10; i++) {
String outData = "" + (i+1);
out.write(outData.getBytes()); //字符流这里换成out.write(outData)
System.out.print(outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ReadData {
public void readMethod(PipedInputStream input) { //字符流这里换成PipedReader
try {
System.out.println("read: ");
byte[] byteArray = new byte[20]; //字符流这里换成char
int readLength = input.read(byteArray);
while (readLength != -1) {
String newData = new String(byteArray, 0, readLength);
System.out.println(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
两个自定义线程
public class ThreadWrite extends Thread {
private WriteData write;
private PipedOutputStream out; //字符流这里换成PipedWriter
public ThreadWrite(WriteData write, PipedOutputStream out) { //字符流这里换成PipedWriter
this.write = write;
this.out = out;
}
public void run() {
write.writeMethod(out);
}
}
public class ThreadRead extends Thread {
private ReadData read;
private PipedInputStream input; //字符流这里换成PipedRead
public ThreadRead(ReadData read, PipedInputStream input) { //字符流这里换成PipedRead
this.read = read;
this.input = input;
}
public void run() {
read.readMethod(input);
}
}
public class Run {
public static void main(String[] args) throws InterruptedException, IOException {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream = new PipedInputStream(); //字符流用PipedReader
PipedOutputStream outputStream = new PipedOutputStream(); //字符流用PipedWriter
inputStream.connect(outputStream); //这两个只能用一个
//outputStream.connect(inputStream);
ThreadRead threadRead = new ThreadRead(readData,inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData,outputStream);
threadWrite.start();
}
}
read:
write :
12345678910
12345678910
使用代码inputStream.connect(outputStream)或outputStream.connect(inputStream)的作用使两个 Stream之间产生通信链接,这样才可以进行输出输入。
二、类ThreadLocal与类InheritableThreadLocal的使用
2.1.类ThreadLocal的使用
变量值的共享可以使用public static 变量的形式,所有的线程都使用同一个public static变量。如果想实现每一个线程都有自己的共享变量该如何解决呢?JDK中提供的类ThreadLocal正是为了解决这样的问题。
类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。
1、方法get()与null
public class Run {
public static ThreadLocal threadLocal = new ThreadLocal();
public static void main(String[] args) throws InterruptedException {
if (threadLocal.get() == null) {
System.out.println("从未放过值。");
threadLocal.set("我的值");
}
System.out.println(threadLocal.get());
}
}
从未放过值。
我的值
从运行结果来看,第一次调用threadLocal对象的get()方法时返回的值是null,通过调用set()方法赋值后顺利取出值并打印到控制台上。
类Threadlocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放人Threadlocal类中进行保存的。
2、验证线程变量的隔离性
多个线程用一个ThreadLocal类
public class Tools {
public static ThreadLocal t1 = new ThreadLocal();
}
// 两个自定义的线程
public class MyThread1 extends Thread {
public void run() {
try {
for (char i='a'; i<'z'; i++) {
Tools.t1.set("Thread1-" + i);
System.out.println("Thread1 get value=" + Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyThread2 extends Thread{
public void run() {
try {
for (int i=0; i<30; i++) {
Tools.t1.set("Thread2—" + (i+1));
System.out.println("Thread2 get value=" + Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread1 a = new MyThread1();
MyThread2 b = new MyThread2();
a.start();
b.start();
for (int i=0; i<33; i++) {
Tools.t1.set("Main" + (i+1));
System.out.println("Main get value=" + Tools.t1.get());
Thread.sleep(200);
}
}
}
Main get value=Main1
Thread2 get value=Thread2—1
Thread1 get value=Thread1-a
Thread1 get value=Thread1-b
Thread2 get value=Thread2—2
Main get value=Main2
Thread1 get value=Thread1-c
Main get value=Main3
Thread2 get value=Thread2—3
……
虽然3个线程都向t1对象中set()数据值,但每个线程还是能取出自己的数据。
3、解决get()返回null的问题
重写ThreadLocal方法,得到初始值,这样第一次get不再为null
public class ThreadLocalExt extends ThreadLocal {
@Override
protected Object initialValue() {
return "重写ThreadLocal方法,得到初始值,让第一次get不再为null";
}
}
public class Run {
public static ThreadLocalExt t1 = new ThreadLocalExt();
public static void main(String[] args) throws InterruptedException {
if (t1.get() == null) {
System.out.println("从未放过值");
t1.set("我的值");
}
System.out.println(t1.get());
}
}
2.2、类InheritableThreadLocal的使用
使用InheritableThreadLocal类可以让子线程从父线程中取得值。
1、值继承
public class InheritableThreadLocal extends ThreadLocal
public class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue() {
return new Date().getTime();
}
}
public class Tools {
public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
public class MyThread1 extends Thread {
public void run() {
try {
for (int i=0; i<5; i++) {
System.out.println("Thread1 get value=" + Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
for (int i=0; i<5; i++) {
System.out.println(" 在Main线程中取值=" + Tools.t1.get());
Thread.sleep(100);
}
Thread.sleep(3000);
MyThread1 a = new MyThread1();
a.start();
}
}
在Main线程中取值=1462588336441
在Main线程中取值=1462588336441
在Main线程中取值=1462588336441
在Main线程中取值=1462588336441
在Main线程中取值=1462588336441
Thread1 get value=1462588336441
Thread1 get value=1462588336441
Thread1 get value=1462588336441
Thread1 get value=1462588336441
Thread1 get value=1462588336441
2、值继承再修改
再继承的同时还可以对值进行进一步的处理(但是在使用InheritableThreadLocal类需要注意一点的是,如果子线程在取得值的同时,主线程将InheritableThreadLocal中的值更改,那么 子线程取到的值还是旧值)。修改如下
public class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue() {
return new Date().getTime();
}
@Override
protected Object childValue(Object parentValue) {
return parentValue + " 重写后在子线程加的!";
}
}
在Main线程中取值=1462588868404
在Main线程中取值=1462588868404
在Main线程中取值=1462588868404
在Main线程中取值=1462588868404
在Main线程中取值=1462588868404
Thread1 get value=1462588868404 重写后在子线程加的!
Thread1 get value=1462588868404 重写后在子线程加的!
Thread1 get value=1462588868404 重写后在子线程加的!
Thread1 get value=1462588868404 重写后在子线程加的!
Thread1 get value=1462588868404 重写后在子线程加的!