为啥 Runnable 中的实例变量在执行时消失了?

Posted

技术标签:

【中文标题】为啥 Runnable 中的实例变量在执行时消失了?【英文标题】:How come the instance variable inside a Runnable is gone when it's executed?为什么 Runnable 中的实例变量在执行时消失了? 【发布时间】:2021-01-03 09:40:17 【问题描述】:
@Autowired
ThreadPoolTaskScheduler taskScheduler;

@Autowired
SendUpdatesRunnable sendUpdatesRunnable;

private void createJob(String timezone) 
        ZonedDateTime zoned = LocalDate.now().atTime(10, 11).atZone(ZoneId.of(timezone));
           
        sendUpdatesRunnable.timezone = timezone;
        taskScheduler.schedule(
                sendUpdatesRunnable,
                Date.from(zoned.toInstant())
        );
    

@Component
public class SendUpdatesRunnable implements Runnable

    @Autowired
    ProductRepository productRepository;

    String timezone;

    @Override
    @Transactional
    public void run() 
        List<Product> newProds = productRepository.findProductByCreateDateTimeIsAfter(LocalDateTime.now().minusHours(24));
        List<Product> updatedProds = productRepository.findProductByUpdateDateTimeIsAfter(LocalDateTime.now().minusHours(24));

       //System print out timezone variable = null
    

【问题讨论】:

你能解释一下你在问什么吗?你能指望什么?会发生什么,何时以及如何发生? 【参考方案1】:

问题

当您注入带有 AOP 内容的 Spring Bean(即@Transcational)时,Spring Framework 不会注入真正的具体类,而是注入代理对象。代理类包含字段timezone,它不会被委托给您的真实类实例。

Spring Framework 中的代理如何工作:

source

存在代理是因为您的 run() 方法看起来像这样:

public void run() 
  transactionManager.begin();
  realInstance.run();
  transactionManager.commit();

因此,当您像 sendUpdatesRunnable.timezone = timezone; 这样设置时区时,实际上是在这样做 proxy.timezone = timezone; 并且您的 sendUpdatesRunnable 对象仍然将 timezone 设为 null。

解决方案

你需要使用getter/setter。

当你使用 setter 方法时,代理类会有这样的实现:

public void setTimezone(String timezone) 
  realInstance.setTimezone(timezone);

因此,当您调用 set 方法时,它将正确传播到具体实现所在的真实实例。

话虽如此,您根本不应该在 Spring Bean 实现中使用实例级别的可变变量。您应该拥有的唯一实例变量是在 bean 初始化时注入的依赖项。

【讨论】:

我的意思是非依赖实例变量。我会更新以澄清。

以上是关于为啥 Runnable 中的实例变量在执行时消失了?的主要内容,如果未能解决你的问题,请参考以下文章

继承Thread类和实现Runnable接口创建的线程对于实例变量和类变量的共享

继承Thread类和实现Runnable接口创建的线程对于实例变量和类变量的共享

在java中,为啥类实例也能访问静态域?

为啥传递给runnable的变量必须是final的?

为啥要实现Runnable接口,来实现多线程?把Thread作为父类则不能呢?

Runnable接口详解?