Java多线程之通过管道线程间通信(字节流、字符流),类ThreadLocal与类InheritableThreadLocal的使用

来源:互联网 发布:java数组按大小排序 编辑:程序博客网 时间:2024/05/21 13:48

一、通过管道线程间通信(字节流、字符流)

  在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 重写后在子线程加的!

阅读全文
0 0
原创粉丝点击