Java多线程:如何在当前线程访问其他线程内的对象数据
这个场景的核心是Java内存模型(JMM)的跨线程可见性问题:运行中的线程对持有的对象实例变量做的修改,默认会先存在线程私有的工作缓存中,没有特殊同步的情况下,发起API调用的线程无法保证读到最新值,甚至可能因为指令重排序读到构造不完整的无效对象。所有可行方案都需要同时解决可见性、有序性问题,如果涉及读改写的复合操作,还要额外保证原子性。
按场景优先级选择即可:
- 变量是基础类型/不可变对象,且只有单线程写、多线程读,不存在
i++这类读改写复合操作:直接用volatile修饰目标实例变量volatile会禁止相关指令重排序,变量修改后强制刷回主内存,其他线程读取时强制从主内存拉取最新值,能保证读到的永远是最后一次写入的结果。注意如果变量是引用类型,volatile仅保证引用本身的可见性,引用指向的对象内部字段修改不受这个语义保护。 - 变量存在多线程同时写、或者涉及复合读写操作:用
synchronized内置锁,或者java.util.concurrent.locks下的显式锁,给变量的所有读、写操作都加上同一把锁锁的happens-before语义保证:进入同步块时,线程会把锁保护的所有变量的最新值从主存加载到工作内存;退出同步块时,所有对变量的修改会强制刷回主存,从根本上解决可见性问题。注意读操作和写操作必须加同一把锁,否则依然会出现可见性问题。
简单实现示例:public class TargetObject { private int changedValue; private final Object syncLock = new Object(); // 持有对象的工作线程调用,修改变量 public void setChangedValue(int newValue) { synchronized (syncLock) { changedValue = newValue; } } // API调用线程调用,读取最新值 public int getChangedValue() { synchronized (syncLock) { return changedValue; } } } - 变量是计数、状态标记、简单引用这类场景:直接用
java.util.concurrent.atomic包下的原子类包装变量,比如AtomicInteger、AtomicReference原子类底层基于CAS操作+volatile语义实现,既保证了读改写复合操作的原子性,也自带跨线程可见性保证,无锁实现的性能比显式加锁更好,不需要自己写同步逻辑。
- 如果不需要在工作线程运行中途实时读值,只需要等工作线程处理完拿最终结果:直接用JUC提供的
Future/CompletableFuture包装工作线程的执行任务这是最省心的实现方式,JUC的异步任务框架已经内置了所有可见性保障,工作线程对结果对象的所有修改,在调用
get()拿到结果后,对当前API线程是完全可见的,不需要额外加同步。
示例代码:// 把工作逻辑提交到线程池 Future<TargetObject> taskFuture = threadPool.submit(() -> { TargetObject obj = new TargetObject(); // 工作线程内执行逻辑、修改对象实例变量 obj.setChangedValue(999); return obj; }); // API调用处需要取值时,直接获取 TargetObject latestObj = taskFuture.get(); int latestValue = latestObj.getChangedValue();
不要直接用普通成员变量、静态变量跨线程传值,没有同步语义保障的情况下,你大概率会随机读到过期值、null甚至构造异常的对象,这类问题偶发、排查成本极高。
内容的提问来源于stack exchange,提问作者shivi

