Java:新线程可跳过同步方法(如果已有另一个线程)

时间:2019-06-26 16:05:03

标签: java spring multithreading

要求

  1. 我需要能够通过POST调用触发(长期运行)作业并立即返回。

  2. 一次只有一个线程可以运行该作业。

  3. 这项工作是一项昂贵的工作,如果一项工作已经在进行中,我希望该工作的所有将来触发因素不执行任何操作。

代码

@RestController
public class SomeTask {

    private SomeService someService;

    @Autowired
    public SomeTask(SomeService someService) {
        this.someService = someService;
    }

    @Async // requirement 1
    @RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
    public void triggerJob() {
        expensiveLongRunningJob();
    }

    /**
     * Synchronized in order to restrict multiple invocations. // requirement 2
     *
     */
    private synchronized void expensiveLongRunningJob() { 
        someService.executedJob();
    }
}

问题

满足上述代码要求1和2。满足要求3的最佳方法是什么(已通过POST调用创建了新线程,跳过了同步方法并在获取锁失败后立即返回)?

2 个答案:

答案 0 :(得分:1)

对于要求1 ,如果您只想使用@Async,则应将其用于服务方法而非控制器方法。但是请注意,通过使其异步,您将失去对作业的控制,并且无法进行故障处理,除非您使用@Async实现Future并通过实现AsyncUncaughtExceptionHandler接口来处理故障。

对于需求3 ,您可以在服务中包含一个易失的布尔字段,该字段在开始作业过程之前就已设置,而在作业过程完成后才被取消。在控制器方法中,您可以检查服务的易失布尔值字段来确定作业是否正在执行,如果作业正在进行,则仅返回适当的消息。另外,在处理AsyncUncaughtExceptionHandler接口实现失败时,请确保取消设置布尔字段。

服务:

@Service
public class SomeService {

    public volatile boolean isJobInProgress = false;

    @Async
    public Future<String> executeJob() {
        isJobInProgress = true;
        //Job processing logic
        isJobInProgress = false;
    }
}

控制器:

@RestController
public class SomeTask {

    @Autowired
    private SomeService someService;

    @RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
    public void triggerJob() {
        if (!someService.isJobInProgress){
            someService.executeJob(); //can have this in a sync block to be on the safer side. 
        } else {
            return;
        }
    }

}

AsyncUncaughtExceptionHandler的实现:

public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Autowired
    private SomeService someService;

    @Override
    public void handleUncaughtException(
            Throwable throwable, Method method, Object... obj) {

        //Handle failure
        if (someService.isJobInProgress){
            someService.isJobInProgress = false;
        }
    }
}

@异步配置:

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }

}

答案 1 :(得分:1)

同步不是完成任务的正确工具。您可以这样做:

@RestController
public class SomeTask {

    private SomeService someService;
    private final AtomicBoolean isTriggered = new AtomicBoolean();

    @Autowired
    public SomeTask(SomeService someService) {
        this.someService = someService;
    }

    @Async // requirement 1
    @RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
    public void triggerJob() {
        if (!isTriggered.getAndSet(true)) {
            try {
                expensiveLongRunningJob();
            } finally {
                isTriggered.set(false);
            }
        }
    }

    /**
     * only runs once at a time, in the thread that sets isTriggered to true
     */
    private void expensiveLongRunningJob() { 
        someService.executedJob();
    }
}