java - 为什么这个方法不同步?

标签 java spring spring-boot

我创建了一个调用“Invokable”服务类的 REST Controller (在 Spring Boot 2.2.2 中),如下所示:

@RestController
@RequestMapping("service")
public class ServiceController {

    private Set<Invokable> services;

    @Autowired
    public ServiceController(Set<Invokable> services) {
        this.services = services;
    }

    @GetMapping("invoke/{service}")
    public void invoke(@PathVariable("service") String service) throws InterruptedException {
        val invokable = services.stream().filter(c -> AopUtils.getTargetClass(c).getName().equals(service)).findFirst();
        invokable.get().invoke();
    }
}

Invokable接口(interface)方法被标记为Async:

public interface Invokable {

    @Async("taskExecutor")
    void invoke();
}

其中一个实现如下所示:

@Service
@Transactional
public class EventListener implements Invokable {

    private static boolean RUNNING = false;

    private EventService eventService;

    private List<EventProcessor> processors;

    @Autowired
    public EventListener(EventService eventService,
                         List<EventProcessor> processors) {
        this.eventService = eventService;
        this.processors = processors;
    }

    @Override
    public void invoke() {
        if (!RUNNING) {

            RUNNING = true;

            [...]

            RUNNING = false;
        }
    }
} 

在上面您可以看到,我通过使用 RUNNING boolean 值锁定方法来实现自定义同步机制。这工作得很好,但我不想自己实现这个。

之前我曾尝试过(通过lombok):

@Override
@Synchronized
public void invoke() {
  ...
}

@Override
public synchronized void invoke() {
  ...
}

@Override
public void invoke() {
  synchronized(this) {
    ...
  }
}

但是,以上所有内容似乎都允许同时调用该方法一次以上。

我是否没有理解synchronized的用途? 有没有一种更干净的方法来实现上述目标,而无需自己实现该机制?

最佳答案

synchronized 可防止并发调用。但这并不意味着运行时进行的调用将被丢弃和忽略。这意味着它将阻塞、等待,直到前一个完成,然后执行。

您的代码没有同步任何内容。它仅忽略运行时进行的调用,这是非常不同的。它以非线程安全的方式执行此操作:同时调用该方法的两个单独的线程可能会看到相同的 boolean 值,并且都同时执行该方法。更糟糕的是:在一个线程中对 boolean 值所做的更改可能在另一个线程中不可见。

您需要正确使用 AtomicBoolean 来以线程安全的方式完成您正在做的事情。

关于java - 为什么这个方法不同步?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59509837/

相关文章:

java - 如何将 portlet 部署到 Liferay?

java - 我尝试将 Spring 的默认记录器更改为 log4j2 有什么问题?

java - 找不到参数的方法实现()

java - 另一个属性键中使用的 Spring-boot 属性值

spring - 使用 oauth2 范围而不是角色来保护 spring 执行器管理端点

java - 在哪里放置逻辑 - 域类、服务类、其他类?

spring-boot - Spring 数据 R2DBC : org. springframework.data.mapping.PropertyReferenceException:找不到类型的属性 findAll

Spring JOOQ - 从控制台中删除获取的结果

java - 有条件地隐藏android中的显示按钮

java - 在另一台机器上运行 JavaFX 应用程序