我创建了一个调用“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/