线程在实际项目中的应用

线程在项目中的应用

1. 发短信

发短信的场景有很多,比如手机号+验证码登录注册,电影票买完之后会发送取票码,发货之后会有物流信息,支付之后银行发的付款信息,电力系统的电费预警信息等等。

在这些业务场景中,有一个特征,主业务和短信业务可以割裂。

比如手机号+验证码登录,当我们点击获取验证码的时候,会连接短信业务平台发短信,但是发短信这个业务受短信平台的影响,可能会有一定的延时,我们不一定非要等短信平台返回之后,在给用户返回。

我们可以先返回获取验证码成功,将发短信的业务放入另一个线程中执行,用户晚一会收到短信对整体的业务流程也不会受到影响,反而提升了用户的体验。

1.1 代码演示

package com.yf.sso.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
    
    

    @Bean
    public Executor asyncServiceExecutor() {
    
    
        log.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(5);
        //配置最大线程数
        executor.setMaxPoolSize(5);
        //配置队列大小
        executor.setQueueCapacity(5);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");
 
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
 
        return executor;
    }
}
@Autowired
    private ThreadService threadService;

    public boolean sendSms(String phone) {
    
    
        /**
         * 1. 调用短信平台 发送短信  如果发送成功,将验证码存入redis,redis要有过期时间
         * 2. 发送成功,返回成功
         */
        //短信验证码 要生成
        int code = RandomUtils.nextInt(100000, 999999);
        log.info("短信验证码:{}",code);
        //放入线程池执行,不影响当前的业务,立马返回
        threadService.sendSMS(phone,code);
        return true;
    }
package com.yf.sso.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class ThreadService {
    
    
   

    @Autowired
    private SmsService smsService;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Async("asyncServiceExecutor")
    public void sendSMS(String phone , int code) {
    
    
        boolean isSend = smsService.send(phone,code);
        if (isSend){
    
     redisTemplate.opsForValue().set("LOGIN_"+phone,String.valueOf(code), Duration.ofMinutes(time));
        }
    }
}

2. 推送

比如有一个业务场景:

有一个审核业务,当收到数据后,需要将这些数据发送给第三方的监管系统进行审核。

数据量级有百万之多,一条数据推送按一秒计算,那么需要过百万秒,200多个小时以上。

解决:

考虑引入多线程进行并发操作,降低数据推送时间,提供数据推送的实时性。

要注意的问题:

  1. 防止重复推送

    可以考虑将数据切分成不同的数据段,每个线程负责一个

  2. 失败处理

    推送失败后,进行失败推送的数据记录,用额外的程序处理失败数据

2.1 代码演示

package com.yf.push.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Service
public class PushService {
    
    

    @Autowired
    private ThreadService threadService;

    public void oldPush(){
    
    
        int dataNum = 10000;
        int[] array = new int[dataNum];
        for (int i = 0;i<dataNum;i++){
    
    
            array[i] = i;
        }
        long start = System.currentTimeMillis();
        //推送的数据数量

        for (int i = 0 ; i < array.length;i++){
    
    
            //推送到第三方审核平台
            pushSend(array[i]);
        }

        long end = System.currentTimeMillis();

        System.out.println((end - start) + "ms");
    }

    @Autowired
    private ThreadPoolTaskExecutor asyncServiceExecutor;

    public void pushNew(){
    
    
        int dataNum = 10000;
        int[] array = new int[dataNum];
        for (int i = 0;i<dataNum;i++){
    
    
            array[i] = i;
        }
        long start = System.currentTimeMillis();
        //推送的数据数量
        //假设线程池数量为10,也就是说 每个线程处理 1000条数据 共需要10次循环
        for (int i = 0 ; i < 10;i++){
    
    
            int s = i * 1000;
            int e = i * 1000 + 1000 - 1;
            //推送到第三方审核平台
            //这个是假设 有10000条数据,那么每次推送处理1000条数据
            threadService.push(array,s,e);
        }

        long end = System.currentTimeMillis();

        System.out.println((end - start) + "ms");
    }

    public void pushNewCall(){
    
    
        int dataNum = 10000;
        int[] array = new int[dataNum];
        for (int i = 0;i<dataNum;i++){
    
    
            array[i] = i;
        }
        long start = System.currentTimeMillis();
        //推送的数据数量
        //假设线程池数量为10,也就是说 每个线程处理 1000条数据 共需要10次循环
        List<Future> futureList = new ArrayList<>();
        for (int i = 0 ; i < 10;i++){
    
    
            int s = i * 1000;
            int e = i * 1000 + 1000 - 1;
            //推送到第三方审核平台
            //这个是假设 有10000条数据,那么每次推送处理1000条数据
            Future<Integer> submit = asyncServiceExecutor.submit(new Callable<Integer>() {
    
    
                @Override
                public Integer call() throws Exception {
    
    
                    return threadService.push1(array, s, e);
                }
            });
            futureList.add(submit);
        }

        for (Future future : futureList) {
    
    
            try {
    
    
                System.out.println("本轮线程执行数量:" +future.get());
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } catch (ExecutionException e) {
    
    
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();

        System.out.println((end - start) + "ms");
    }

    private void pushSend(int data) {
    
    
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

package com.yf.push.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class ThreadService {
    
    


    @Async("asyncServiceExecutor")
    public void push(int[] array, int start, int end){
    
    
        long s = System.currentTimeMillis();
        for (int i = start;i<=end;i++){
    
    
            pushSend(array[i]);
            //推送失败 可以记录日志
        }
        long e = System.currentTimeMillis();
        System.out.println((e-s)+"ms");
    }

    public int push1(int[] array, int start, int end){
    
    
        int count = 0;
        long s = System.currentTimeMillis();
        for (int i = start;i<=end;i++){
    
    
            count++;
            pushSend(array[i]);
            //推送失败 可以记录日志
        }
        long e = System.currentTimeMillis();
        System.out.println((e-s)+"ms");
        return count;
    }

    public void pushSend(int dataNum){
    
    
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

测试:

package com.yf.push.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.TimeUnit;

@SpringBootTest
public class TestPushService {
    
    


    @Autowired
    private PushService pushService;

    @Test
    public void testOldPush(){
    
    
        pushService.oldPush();
    }

    @Test
    public void testNewPush(){
    
    
        pushService.pushNew();
        try {
    
    
            TimeUnit.HOURS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

    @Test
    public void testNewPushCall(){
    
    
        pushService.pushNewCall();
        try {
    
    
            TimeUnit.HOURS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_51250404/article/details/121601946