线程数据通信

  • 使用共享变量/对象
  • 父子线程通信
  • Exchanger
  • 管道



我们知道Java每个线程之间是数据隔离的,那在多线程环境下,两个线程之间,如何进行数据传输呢

下面我们以main线程中新起一个子线程的方式,来模拟两个线程之间数据通信的场景。

使用共享变量/对象

private static String name = "张三";

    public static void main(String[] args) {

        System.out.println("主线程:name :" + name);

        new Thread(() -> {
            System.out.println("子线程:name :" + name);
        }).start();
    }

当多个线程读取同一个变量/对象时,当其中一个线程修改了 此变量/对象,下次另一个线程在读取时便可以获取到更改后的值。

但是需要注意:

  1. 当多个线程共享同一个数据时,需要考虑并发修改的情况,每一次修改只能由一个线程操作,此时需要对数据进行加锁
  2. 每个线程修改数据,实际修改的是本线程栈中的数据副本,应该使用volatile让其修改对其他线程可见,避免其他线程依旧读取数据副本

父子线程通信

如果两个线程之间是父子关系,可以使用InheritableThreadLocal将父线程中存储的信息传给子线程:

public class MyThreadData {

    public static ThreadLocal<String> local = new ThreadLocal<>();

    public static InheritableThreadLocal<String> inhLocal = new InheritableThreadLocal<>();


    public static String getLocal() {
        return local.get();
    }

    public static String getinhLocal() {
        return inhLocal.get();
    }

}

我们定义一个MyThreadData类,里面有两个变量:ThreadLocal,和InheritableThreadLocal,下面我们对这两种local做下测试

java给线程传递参数吗 java线程之间传递数据_子线程


主线程中分别往ThreadLocal和InheritableThreadLocal中存入name"张三"和"李四"。

我们在子线程中通过读取ThreadLocal并没有获取到信息,但是通过读取InheritableThreadLocal则可以获取到信息。

这是因为ThreadLocal是线程私有的,必须当前线程才能访问到数据,而InheritableThreadLocal则可以将数据传递给子线程。为什么会这样呢?我们来看下一个线程启动时做了哪些事件:

java给线程传递参数吗 java线程之间传递数据_子线程_02


我们定位到Thread这个类的init方法,可以看到在线程create的时候,会去判断parent.inheritableThreadLocals是否为null,这个parent自然就是我们的主线程了,在不为null的情况下会去调用ThreadLocal.createInheritedMap()方法,入参便是主线程的inheritableThreadLocals对象。我们再看下createInheritedMap这个方法做了什么事情:

java给线程传递参数吗 java线程之间传递数据_主线程_03


java给线程传递参数吗 java线程之间传递数据_主线程_04


这边我们可以看到,最终会去遍历父线程inheritableThreadLocals里面的数据,在子线程中依据父线程数据创建出一个新的entry对象。这样在子线程中自然就持有了父线程数据,且之后两套数据相互独立。

Exchanger

我们也可以使用JDK提供的Exchanger进行数据交换:

java给线程传递参数吗 java线程之间传递数据_子线程_05


通过demo我们可以看到两个线程之间,相互交换了数据。

java给线程传递参数吗 java线程之间传递数据_子线程_06


Exchanger ,可以看成是有两个格子,当两个格子被填满之后,进行数据交换。但是需要注意的是,exchange方法,可以称为交换点,这个方法是阻塞的。如果一个线程已经执行到了exchange方法,会阻塞在这边等待另一个线程也执行exchange方法。

当然我们也可以设置超时时间:

java给线程传递参数吗 java线程之间传递数据_子线程_07

管道

java给线程传递参数吗 java线程之间传递数据_数据_08


需要定义管道的输入流和输出流,主线程向管道中写入"hello",另一个线程从管道中读取出“hello”。