当前位置: 面试刷题>> Java 中父子线程之间如何传递数据?


在Java中,父子线程间的数据传递是一个常见的需求,尤其在处理复杂的多线程应用时显得尤为关键。作为高级程序员,我们不仅要了解基本的线程同步机制,如synchronized关键字、wait()/notify()/notifyAll()方法,还要掌握更高级的并发工具,如java.util.concurrent包下的类。下面,我将从几个层面详细介绍如何在Java中实现父子线程间的数据传递,并附上示例代码。

1. 使用共享变量

最直接的方式是使用共享变量,但这种方式需要谨慎处理同步问题,以避免数据不一致。

示例代码

class SharedData {
    private int value;

    public synchronized void setValue(int value) {
        this.value = value;
    }

    public synchronized int getValue() {
        return this.value;
    }
}

class ChildThread extends Thread {
    private SharedData data;

    public ChildThread(SharedData data) {
        this.data = data;
    }

    @Override
    public void run() {
        // 假设子线程修改了数据
        data.setValue(100);
    }
}

public class ParentThreadExample {
    public static void main(String[] args) throws InterruptedException {
        SharedData data = new SharedData();
        ChildThread child = new ChildThread(data);
        child.start();
        child.join(); // 等待子线程执行完毕

        // 父线程读取数据
        System.out.println("Value from child: " + data.getValue());
    }
}

2. 使用CallableFuture

对于需要返回结果的场景,Callable接口比Runnable更合适,因为Callable可以抛出异常并返回结果。结合FutureTaskExecutorService可以优雅地处理这些返回值。

示例代码

import java.util.concurrent.*;

class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 模拟耗时计算
        Thread.sleep(1000);
        return 42; // 假设这是计算结果
    }
}

public class CallableExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> future = executor.submit(new Task());

        // 父线程可以继续做其他事情...

        // 获取子线程的计算结果
        Integer result = future.get(); // 阻塞直到结果可用
        System.out.println("Result from child: " + result);

        executor.shutdown();
    }
}

3. 使用管道(Piped Streams)

对于字符流数据的传递,可以使用Java的管道(Piped Streams)。这种方式适用于需要连续传输大量数据流的场景。

注意:管道通常用于I/O操作,但理论上也可用于线程间通信。

4. 通过线程间通信(如BlockingQueue

java.util.concurrent包中的BlockingQueue提供了一种线程安全的队列实现,非常适合用于生产者-消费者模型中的线程间通信。

示例代码

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class Producer implements Runnable {
    private BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            queue.put(123); // 生产数据
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

// 消费者类似,这里省略

public class BlockingQueueExample {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
        Producer producer = new Producer(queue);
        Thread producerThread = new Thread(producer);

        producerThread.start();
        producerThread.join();

        // 父线程从队列中获取数据
        Integer data = queue.take(); // 阻塞直到有数据可取
        System.out.println("Data from child: " + data);
    }
}

总结

在Java中,父子线程间的数据传递可以通过多种方式实现,每种方式都有其适用场景。作为高级程序员,应根据具体需求和环境选择最合适的方案。在上述示例中,CallableFuture提供了灵活的结果传递机制,而BlockingQueue则适用于需要高效队列操作的场景。通过合理使用这些高级并发工具,我们可以编写出既高效又易于维护的多线程应用。此外,在设计和实现这些机制时,务必考虑线程安全和数据一致性的问题。

推荐面试题