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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值