代码之家  ›  专栏  ›  技术社区  ›  crmepham

为什么这个方法不同步?

  •  0
  • crmepham  · 技术社区  · 6 年前

    我已经创建了一个REST控制器(在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 接口方法已标记 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 布尔值。这很好,但我不想自己实现。

    以前我试过 lombok ):

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

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

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

    但是,上面所有的这些似乎都允许同时调用多个方法。

    我错过了明白什么吗 synchronized 是用来做的吗? 有没有一种更清洁的方法来实现上述目标而不亲自实施这一机制?

    1 回复  |  直到 6 年前
        1
  •  1
  •   JB Nizet    6 年前

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

    你的代码没有同步任何东西。它只忽略在运行时进行的调用,这是非常不同的。它以一种非线程安全的方式执行:两个同时调用该方法的独立线程可能会看到相同的布尔值,并且两个线程同时执行该方法。更糟糕的是:在一个线程中对布尔值所做的更改在另一个线程中可能看不到。

    您需要正确地使用AtomicBoolean以线程安全的方式执行您正在执行的操作。