Java高频面试之并发编程-11
hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶
面试官:父子线程如何共享数据?
在Java中,父子线程共享数据可以通过以下几种方式实现,具体选择取决于应用场景和需求:
1. 通过共享对象成员变量
父线程和子线程共享同一个对象的成员变量,需使用同步机制确保线程安全。
class SharedData {private int value;public synchronized void setValue(int value) { this.value = value; }public synchronized int getValue() { return value; }
}public class Main {public static void main(String[] args) {SharedData data = new SharedData();data.setValue(100);Thread childThread = new Thread(() -> {System.out.println("子线程读取数据: " + data.getValue()); // 输出 100});childThread.start();}
}
注意事项:
- 使用
synchronized
或Lock
确保原子性。 - 使用
volatile
保证可见性(适用于简单变量的读写)。
2. 通过构造器参数传递初始数据
父线程在创建子线程时,通过构造器或 Runnable
传递数据。
class ChildThread implements Runnable {private final String message;public ChildThread(String message) {this.message = message;}@Overridepublic void run() {System.out.println("子线程接收消息: " + message);}
}public class Main {public static void main(String[] args) {String message = "Hello from parent";Thread childThread = new Thread(new ChildThread(message));childThread.start();}
}
适用场景:初始化时传递数据,后续无动态更新。
3. 使用线程安全的数据结构
通过 ConcurrentHashMap
、BlockingQueue
等并发容器共享数据。
import java.util.concurrent.ConcurrentHashMap;public class Main {private static ConcurrentHashMap<String, String> sharedMap = new ConcurrentHashMap<>();public static void main(String[] args) {sharedMap.put("key", "初始值");Thread childThread = new Thread(() -> {sharedMap.put("key", "子线程修改后的值");});childThread.start();try {childThread.join(); // 等待子线程结束} catch (InterruptedException e) {e.printStackTrace();}System.out.println("父线程读取数据: " + sharedMap.get("key")); // 输出 "子线程修改后的值"}
}
优点:无需显式同步,高并发性能好。
4. 使用 InheritableThreadLocal
子线程继承父线程的线程局部变量(适用于传递初始化值)。
public class Main {private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("父线程设置的值");Thread childThread = new Thread(() -> {System.out.println("子线程读取数据: " + inheritableThreadLocal.get()); // 输出 "父线程设置的值"});childThread.start();}
}
限制:子线程创建后,父线程对 InheritableThreadLocal
的修改不会影响子线程。
5. 使用 Future
和 Callable
父线程通过 Future
获取子线程的执行结果。
import java.util.concurrent.*;public class Main {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executor = Executors.newSingleThreadExecutor();Future<Integer> future = executor.submit(() -> {return 42; // 子线程计算结果});int result = future.get(); // 父线程阻塞等待结果System.out.println("父线程获取结果: " + result); // 输出 42executor.shutdown();}
}
适用场景:需要异步获取子线程执行结果。
6. 使用回调(Callback)机制
子线程完成任务后通过回调接口通知父线程。
interface Callback {void onComplete(String result);
}class ChildThread implements Runnable {private final Callback callback;public ChildThread(Callback callback) {this.callback = callback;}@Overridepublic void run() {String result = "处理完成";callback.onComplete(result);}
}public class Main {public static void main(String[] args) {new Thread(new ChildThread(result -> {System.out.println("父线程接收回调结果: " + result); // 输出 "处理完成"})).start();}
}
总结与选型建议
方式 | 适用场景 | 线程安全要求 | 灵活性 |
---|---|---|---|
共享对象成员变量 | 简单数据共享,需频繁更新 | 高(需同步) | 中 |
构造器参数传递 | 初始化时传递数据 | 低(仅初始化) | 低 |
线程安全数据结构 | 高并发环境下的数据共享 | 低(容器内部已处理) | 高 |
InheritableThreadLocal | 传递线程局部初始化值 | 低(仅初始化) | 中 |
Future/Callable | 异步获取子线程结果 | 无(结果单向传递) | 中 |
回调机制 | 异步通知父线程 | 低(回调方法需线程安全) | 高 |
注意事项:
- 可见性与原子性:共享变量需使用
volatile
或同步机制确保可见性,复合操作需保证原子性。 - 资源释放:使用线程池时,确保及时关闭并清理资源(如
ExecutorService.shutdown()
)。 - 避免死锁:合理设计锁的获取顺序,避免嵌套锁竞争。