Процесс обучения параллельному программированию SpringBoot (абсолютно сухие товары)

Java задняя часть Безопасность

Если проект всегда использует для запуска один поток, он неизбежно столкнется с некоторыми проблемами производительности, поэтому при переразработке следует стараться максимально использовать многопоточность (в случае обеспечения безопасности потоков).

Примерное содержание этого урока:

  1. Моделирование однопоточного сюжета
  2. Параллельное программирование с Callable
  3. Асинхронная обработка с DeferredResult

Моделирование однопоточного сюжета

/**
 * Created by Fant.J.
 */
@RestController
@Slf4j
public class AsyncController {

    /**
     * 单线程测试
     * @return
     * @throws InterruptedException
     */
    @RequestMapping("/order")
    public String order() throws InterruptedException {
        log.info("主线程开始");
        Thread.sleep(1000);
        log.info("主线程返回");
        return "success";
    }
}

Примем остаток потока за одну секунду как время, необходимое для имитации обработки дела. Очевидно, что это один поток.

nio-8080-exec-1Представляет поток 1 основного потока.

Параллельное программирование с Callable

    /**
     * 用Callable实现异步
     * @return
     * @throws InterruptedException
     */
    @RequestMapping("/orderAsync")
    public Callable orderAsync() throws InterruptedException {
        log.info("主线程开始");
        Callable result = new Callable() {
            @Override
            public Object call() throws Exception {
                log.info("副线程开始");
                Thread.sleep(1000);
                log.info("副线程返回");
                return "success";
            }
        };
        log.info("主线程返回");
        return result;
    }

Мы видим, что запуск и возврат (завершение обработки) основного потока выполняются первыми, а затем вторичный поток выполняет реальную бизнес-обработку. Объясните, что роль основного потока здесь состоит в том, чтобы вызывать (разбудить) дочерний поток, а дочерний поток будет возвращать объект Object после обработки, а затем возвращать его пользователю.

Хотя параллельная обработка достигается таким образом, существует проблема, заключающаяся в том, что основной поток и вторичный поток не полностью разделены, в конце концов, они являются вложенными вторичными потоками.

Поэтому, чтобы оптимизировать нашу реализацию, я здесь имитирую промежуточное ПО для сообщений, чтобы добиться полного разделения основного потока и вторичного потока.

Асинхронная обработка с DeferredResult

Поскольку в этой главе в основном рассказывается о принципе параллельного программирования, здесь мы не используем готовую очередь сообщений, мы моделируем очередь сообщений для обработки.

MockQueue .java
/**
 * 模拟消息队列 类
 * Created by Fant.J.
 */
@Component
@Slf4j
public class MockQueue {

    //下单消息
    private String placeOrder;
    //订单完成消息
    private String completeOrder;

    public String getPlaceOrder() {
        return placeOrder;
    }

    public void setPlaceOrder(String placeOrder) throws InterruptedException {
        new Thread(()->{
            log.info("接到下单请求"+placeOrder);
            //模拟处理
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //给completeOrder赋值
            this.completeOrder = placeOrder;
            log.info("下单请求处理完毕"+placeOrder);
        }).start();
    }

    public String getCompleteOrder() {
        return completeOrder;
    }

    public void setCompleteOrder(String completeOrder) {
        this.completeOrder = completeOrder;
    }
}

Обратите внимание, что в методе setPlaceOrder(String placeOrder) я создал новый поток для обработки операции получения заказа (зачем мне создавать новый поток, я боюсь, что здесь будет зависать основной поток, в эта логика и асинхронная обработка быстрее). Передаваемый параметр — это номер заказа. После 1 с успешной обработки номер заказа передается в поле completeOrder, указывая на то, что пользователь успешно разместил заказ. Я заплачу приведенный ниже код, чтобы контроллер вызвал этот метод.

    //注入模拟消息队列类
    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private DeferredResultHolder deferredResultHolder;
    ....
    @RequestMapping("/orderMockQueue")
    public DeferredResult orderQueue() throws InterruptedException {
        log.info("主线程开始");

        //随机生成8位数
        String orderNumber = RandomStringUtils.randomNumeric(8);
        mockQueue.setPlaceOrder(orderNumber);

        DeferredResult result = new DeferredResult();
        deferredResultHolder.getMap().put(orderNumber,result);
        Thread.sleep(1000);
        log.info("主线程返回");

        return result;
    }

Ну, тогда нам еще нужен класс-посредник для хранения номера заказа и результатов обработки. Зачем нам нужен такой класс?Потому что мы сказали ранее, что хотим разделить основной поток и вторичный поток, поэтому нам нужен посредник для хранения информации об обработке (такой как информация о номере заказа и информация о результатах обработки), и мы судим является ли результат обработки пустым.Просто знайте, выполняется ли вторичный поток или нет. Итак, мы пишем промежуточный класс DeferredResultHolder.

######DeferredResultHolder .java


/**
 *  订单处理情况 中介/持有者
 * Created by Fant.J.
 */
@Component
public class DeferredResultHolder {

    /**
     * String: 订单号
     * DeferredResult:处理结果
     */
    private Map<String,DeferredResult> map = new HashMap<>();

    public Map<String, DeferredResult> getMap() {
        return map;
    }

    public void setMap(Map<String, DeferredResult> map) {
        this.map = map;
    }
}

Повторяю -.-, зачем вам такой класс, ведь мы говорили ранее, что основной поток и вторичный поток разделены, поэтому нужен посредник для хранения информации об обработке (например: информация о номере заказа, а результат обработки информация), Заказ должен соответствовать результату. Иначе будет бардак.

DeferredResult — это объект, используемый для размещения результата обработки.

Что ж, снова возникает новый вопрос, как мы судим, успешна обработка заказа или нет, нам нужно написать слушатель в это время, и отслеживать, есть ли значение в completeOrder в классе MockQueue каждые 100 миллисекунд, если есть значение, то этот заказ нужно обработать. Пишем слушателя.

QueueListener .java

/**
 * Queue监听器
 * Created by Fant.J.
 */
@Component
@Slf4j
public class QueueListener implements ApplicationListener<ContextRefreshedEvent>{

    @Autowired
    private MockQueue mockQueue;

    @Autowired
    private DeferredResultHolder deferredResultHolder;


    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

        new Thread(()->{
            while(true){
                //判断CompleteOrder字段是否是空
                if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())){

                    String orderNumber = mockQueue.getCompleteOrder();

                    deferredResultHolder.getMap().get(orderNumber).setResult("place order success");

                    log.info("返回订单处理结果");

                    //将CompleteOrder设为空,表示处理成功
                    mockQueue.setCompleteOrder(null);
                }else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

Мы видим, что всего нужно обработать три разных потока.



После разделения линии я привезу вам еще одну партию галантерейных товаров, заказной пул ниток https://www.jianshu.com/p/832f2b162450

Узнав об этом, посмотрите на следующее. .

В нашем предыдущем коде есть две части, которые используют new Thread() для создания потоков. После того, как у нас есть собственный пул потоков, мы можем использовать пул потоков для распределения задач потока. Я говорил об этом в пользовательском потоке, я использую Второй метод настройки (с аннотацией @Async для потока). изменить, как показано ниже:

    @Async
    public void setPlaceOrder(String placeOrder) throws InterruptedException {
            log.info("接到下单请求"+placeOrder);
            //模拟处理
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //给completeOrder赋值
            this.completeOrder = placeOrder;
            log.info("下单请求处理完毕"+placeOrder);
    }

Посмотрим на эффект:

Красный кружок — это поток, выделенный в нашем собственном определенном пуле потоков.

Спасибо вам всем!

Представляю все мои коллекции:

популярная рамка

SpringCloud

springboot

nginx

redis

Основной принцип реализации:

Учебник по Java NIO

Подробное объяснение отражения Java

Заметки по изучению параллелизма в Java

Учебник по сервлетам Java

Подробное объяснение компонентов jdbc

Учебник по Java NIO

Исследование языка/версии Java