SpringBoot как элегантно использовать многопоточность

Spring Boot

В этой статье вы быстро поймете использование аннотаций @Async, в том числе асинхронный метод не возвращает значение, возвращаемое значение и, наконец, несколько ошибок аннотации pit @Async.

В приложениях SpringBoot, часто встречающихся в интерфейсе, при этом делаются вещи 1, 2 вещи, 3 вещи, если выполняются синхронно, то это время зависит от того, какой интерфейс 123 и время выполнения; если три вещи тем временем выполняются, это время зависит от того, что интерфейс 123 для выполнения самого длительного времени, рациональное использование многопоточности, может значительно сократить время интерфейсов. Так насколько элегантно использование многопоточных приложений в SpringBoot?

Don't bb, show me code.

быстрый в использовании

Приложения Scramboot должны быть добавлены@EnableAsyncАннотации используются для запуска асинхронных вызовов.Как правило, также настраивается пул потоков.Асинхронный метод передается для завершения в определенный пул потоков следующим образом:

@Configuration
@EnableAsync
public class AsyncConfiguration {

    @Bean("doSomethingExecutor")
    public Executor doSomethingExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(20);
        // 缓冲队列:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("do-something-");
        // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        executor.initialize();
        return executor;
    }
    
}

Способ его использования очень прост, добавьте его в метод, который должен быть асинхронным.@Asyncаннотация

@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/open/something")
    public String something() {
        int count = 10;
        for (int i = 0; i < count; i++) {
            asyncService.doSomething("index = " + i);
        }
        return "success";
    }
}


@Slf4j
@Service
public class AsyncService {

    // 指定使用beanname为doSomethingExecutor的线程池
    @Async("doSomethingExecutor")
    public String doSomething(String message) {
        log.info("do something, message={}", message);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            log.error("do something error: ", e);
        }
        return message;
    }
}

Доступ: 127.0.0.1:8080/open/something, лог такой

2020-04-19 23:42:42.486  INFO 21168 --- [io-8200-exec-17] x.g.b.system.controller.AsyncController  : do something end, time 8 milliseconds
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-1] x.gits.boot.system.service.AsyncService  : do something, message=index = 0
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-5] x.gits.boot.system.service.AsyncService  : do something, message=index = 4
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-4] x.gits.boot.system.service.AsyncService  : do something, message=index = 3
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-6] x.gits.boot.system.service.AsyncService  : do something, message=index = 5
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-9] x.gits.boot.system.service.AsyncService  : do something, message=index = 8
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-8] x.gits.boot.system.service.AsyncService  : do something, message=index = 7
2020-04-19 23:42:42.488  INFO 21168 --- [do-something-10] x.gits.boot.system.service.AsyncService  : do something, message=index = 9
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-7] x.gits.boot.system.service.AsyncService  : do something, message=index = 6
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-2] x.gits.boot.system.service.AsyncService  : do something, message=index = 1
2020-04-19 23:42:42.488  INFO 21168 --- [ do-something-3] x.gits.boot.system.service.AsyncService  : do something, message=index = 2

Видно, что эффект асинхронного выполнения достигнут, и используется настроенный нами пул потоков.

Получить возвращаемое значение асинхронного метода

Когда асинхронный метод имеет возвращаемое значение, как получить возвращаемый результат выполнения асинхронного метода? В настоящее время метод, который необходимо вызвать асинхронно, имеет возвращаемое значение CompletableFuture.

CompletableFuture — это усовершенствование Feature. Feature может обрабатывать только простые асинхронные задачи, а CompletableFuture может выполнять сложные комбинации нескольких асинхронных задач. следующее:

@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @SneakyThrows
    @ApiOperation("异步 有返回值")
    @GetMapping("/open/somethings")
    public String somethings() {
        CompletableFuture<String> createOrder = asyncService.doSomething1("create order");
        CompletableFuture<String> reduceAccount = asyncService.doSomething2("reduce account");
        CompletableFuture<String> saveLog = asyncService.doSomething3("save log");
        
        // 等待所有任务都执行完
        CompletableFuture.allOf(createOrder, reduceAccount, saveLog).join();
        // 获取每个任务的返回结果
        String result = createOrder.get() + reduceAccount.get() + saveLog.get();
        return result;
    }
}


@Slf4j
@Service
public class AsyncService {

    @Async("doSomethingExecutor")
    public CompletableFuture<String> doSomething1(String message) throws InterruptedException {
        log.info("do something1: {}", message);
        Thread.sleep(1000);
        return CompletableFuture.completedFuture("do something1: " + message);
    }

    @Async("doSomethingExecutor")
    public CompletableFuture<String> doSomething2(String message) throws InterruptedException {
        log.info("do something2: {}", message);
        Thread.sleep(1000);
        return CompletableFuture.completedFuture("; do something2: " + message);
    }

    @Async("doSomethingExecutor")
    public CompletableFuture<String> doSomething3(String message) throws InterruptedException {
        log.info("do something3: {}", message);
        Thread.sleep(1000);
        return CompletableFuture.completedFuture("; do something3: " + message);
    }
}

интерфейс доступа

C:\Users\Administrator>curl -X GET "http://localhost:8200/open/somethings" -H "accept: */*"
do something1: create order; do something2: reduce account; do something3: save log

Журналы ключей на консоли выглядят следующим образом:

2020-04-20 00:27:42.238  INFO 5672 --- [ do-something-3] x.gits.boot.system.service.AsyncService  : do something3: save log
2020-04-20 00:27:42.238  INFO 5672 --- [ do-something-2] x.gits.boot.system.service.AsyncService  : do something2: reduce account
2020-04-20 00:27:42.238  INFO 5672 --- [ do-something-1] x.gits.boot.system.service.AsyncService  : do something1: create order

Меры предосторожности

@AsyncАннотация не будет работать в следующих сценариях, то есть она явно используется@AsyncАннотация, но нет многопоточности.

  • Асинхронные методы модифицируются с помощью ключевого слова static;
  • Асинхронный класс не является компонентом-контейнером Spring (обычно с использованием аннотаций@Componentи@Service, и может быть просканировано Spring);
  • Не добавлено в приложение SpringBoot@EnableAsyncаннотация;
  • В том же классе, если метод вызывает другой метод с аннотацией @Async, аннотация не вступит в силу. Причина в том, что аннотированный метод @Async выполняется в прокси-классе.

нужно знать, это: Возвращаемое значение асинхронного метода с использованием аннотации @Async может быть только void или Future и его подклассы. Когда возвращаемый результат относится к другим типам, метод по-прежнему будет выполняться асинхронно, но возвращаемое значение равно null. Некоторые исходные коды выглядят как следует:

AsyncExecutionInterceptor#invoke

image.png

В приведенных выше примерах @Async фактически выполняется асинхронно через Future или CompletableFuture, и Spring снова инкапсулирует его, чтобы нам было удобнее его использовать.

Пример кода для этой статьи:git ee.com/songyin/…, найдите AsyncController.


Поиск в WeChat »читать рыбалка YY», впервые читайте качественные оригинальные статьи.

Оригинальность непростая.До конца ставьте лайк этой статье, большое спасибо.