Spring Boot实践八--用户管理系统(下)

step3:多线程task

首先,实现两个UserService和AsyncUserService两个服务接口:

接口:

java 复制代码
package com.example.demospringboot.service;

public interface UserService {
    void checkUserStatus();
}
java 复制代码
package com.example.demospringboot.service;

public interface AsyncUserService {
    void checkUserStatus();
}

对应实现:

java 复制代码
package com.example.demospringboot.service.impl;

import com.example.demospringboot.bean.User;
import com.example.demospringboot.service.UserService;
import com.example.demospringboot.dao.UserMapper;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void checkUserStatus() {
        List<User> AllUsers = userMapper.findAllUsers();
        for (User u : AllUsers) {
           // System.out.println(ThreadUtils.getThreadName() + ": " + u);
            log.info("{}", u);
        }
    };
}
java 复制代码
package com.example.demospringboot.service.impl;

import com.example.demospringboot.task.AsyncTasks;
import com.example.demospringboot.service.AsyncUserService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AsyncUserServiceImpl implements AsyncUserService {

    @Autowired
    private AsyncTasks asyncTasks;

    @Override
    public void checkUserStatus() {
        asyncTasks.doTaskOne("1");
        asyncTasks.doTaskOne("2");
        asyncTasks.doTaskOne("3");
    };
}

用到的task类如下:

java 复制代码
package com.example.demospringboot.task;

import com.example.demospringboot.utils.ThreadUtils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Component
public class AsyncTasks {
    public static Random random = new Random();

    // @Async注解中的参数就是异步任务的线程池
   @Async("taskExecutor")
    public CompletableFuture<String> doTaskOne(String taskNo){
        log.info("开始任务:{}", taskNo);
        long start = System.currentTimeMillis();
        ThreadUtils.sleepUtil(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务:{},耗时:{} 毫秒", taskNo, end - start);
        return CompletableFuture.completedFuture("任务完成");
    }

}

(1)异步任务通过方法上的@Async("taskExecutor")和启动类的@EnableAsync注解实现,@Async中的参数指定了异步任务使用的的线程池。调用异步方法时不会等待方法执行完,调用即过,被调用方法在自己的线程池中奔跑。

(2)多线程执行的返回值是Future类型或void。Future是非序列化的,微服务架构中有可能传递失败。spring boot推荐使用的CompletableFuture来返回异步调用的结果。

用到的thread工具类如下:

java 复制代码
package com.example.demospringboot.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Repository;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

@Repository
public class ThreadUtils {
    public static final int MAX_POOL_SIZE = 2;
    public static final String EXECUTOR_POOL_PREFIX = "exe-" + MAX_POOL_SIZE + "-";
    public static final String ASYNC_EXECUTOR_POOL_PREFIX = "async-exe-" + MAX_POOL_SIZE + "-";

    public static final String ASYNC_TASK_POOL_PREFIX = "async-task-" + MAX_POOL_SIZE + "-";

    // 自定义AsyncTask线程池
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(MAX_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(MAX_POOL_SIZE);
        executor.setKeepAliveSeconds(0);
        executor.setThreadNamePrefix(ASYNC_TASK_POOL_PREFIX);
        // 如果添加到线程池失败,那么主线程会自己去执行该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    // 启动Executor的线程池
    public static ThreadPoolTaskExecutor getThreadPool(String threadNamePrefix) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(MAX_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(MAX_POOL_SIZE);
        executor.setKeepAliveSeconds(0);
        executor.setThreadNamePrefix(threadNamePrefix);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();

        return executor;
    }
    public static void sleepUtil(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            System.out.println(e);
        }
    }
}

线程池用的是ThreadPoolTaskExecutor 。Executor 顾名思义是专门用来处理多线程相关的一个接口,所有线程相关的类都实现了这个接口,里面有一个execute()方法,用来执行线程,线程池主要提供一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁的额外开销,提高了响应的速度。

ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理,是spring core包中提供的,而ThreadPoolExecutor是JDK中的JUC。

参数说明:

  • corePoolSize:核心线程数
  • queueCapacity:任务队列容量(阻塞队列)
  • maxPoolSize:最大线程数
  • keepAliveTime:线程空闲时间
  • rejectedExecutionHandler:任务拒绝处理器
    异步任务会先占用核心线程,核心线程满了其他任务进入队列等待;在缓冲队列也满了之后才会申请超过核心线程数的线程来进行处理。当线程数已经达到maxPoolSize,且队列已满,线程池可以调用这四个策略处理:
    • AbortPolicy策略:默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
    • DiscardPolicy策略:如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。
    • DiscardOldestPolicy策略:如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。
    • CallerRunsPolicy策略:如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行。
    • 也可以自己实现RejectedExecutionHandler接口,可自定义处理器

为了控制异步任务的并发不影响到应用的正常运作,我们必须要对线程池做好相应的配置,防止资源的过渡使用。需考虑好默认线程池的配置和多任务情况下的线程池隔离。

上述服务我们就用不同线程池的两个WorkManager进行管理:

java 复制代码
package com.example.demospringboot.workmanager;

import com.example.demospringboot.service.UserService;
import com.example.demospringboot.utils.ThreadUtils;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class WorkManager {
    private static final ThreadPoolTaskExecutor EXECUTOR_POOL =
            ThreadUtils.getThreadPool(ThreadUtils.EXECUTOR_POOL_PREFIX);

    @Autowired
    private UserService userService;

    public void startExecutor() {
        EXECUTOR_POOL.execute(new Executor(userService));
    }

     static class Executor implements Runnable {
        private UserService userService;

        public Executor(UserService userService) {
            this.userService = userService;
        }

        @Override
        public void run() {
            while (true) {
                userService.checkUserStatus();
                // sleep 1s
                ThreadUtils.sleepUtil(1000L);
            }
        }
    }
}
java 复制代码
package com.example.demospringboot.workmanager;

import com.example.demospringboot.service.AsyncUserService;
import com.example.demospringboot.utils.ThreadUtils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class AsyncWorkManager {

    private static final ThreadPoolTaskExecutor ASYNC_EXECUTOR_POOL =
            ThreadUtils.getThreadPool(ThreadUtils.ASYNC_EXECUTOR_POOL_PREFIX);

    @Autowired
    private AsyncUserService asyncUserService;

    public void startSyncExecutor() {
        ASYNC_EXECUTOR_POOL.execute(new AsyncExecutor(asyncUserService));
    }

    static class AsyncExecutor implements Runnable {
        private AsyncUserService asyncUserService;

        public AsyncExecutor(AsyncUserService asyncUserService) {
            this.asyncUserService = asyncUserService;
        }

        @Override
        public void run() {
          while (true) {
                asyncUserService.checkUserStatus();
               // sleep 1s
               ThreadUtils.sleepUtil(1000L);
           }
        }
    }
}

主类如下:

java 复制代码
package com.example.demospringboot;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

import org.springframework.cache.annotation.EnableCaching;

import com.example.demospringboot.workmanager.WorkManager;
import com.example.demospringboot.workmanager.AsyncWorkManager;

@EnableCaching
@EnableAsync
@SpringBootApplication
@MapperScan(value = {"com.example.demospringboot.dao"})
public class DemospringbootApplication implements CommandLineRunner {
    @Autowired
    private WorkManager workManager;

    @Autowired
    private AsyncWorkManager asyncWorkManager;

    public static void main(String[] args) {
        SpringApplication.run(DemospringbootApplication.class, args);
    }

    @Override
    public void run(String... strings) {
       //workManager.startExecutor();
       asyncWorkManager.startSyncExecutor();
    }
}

主启动类实现了CommandLineRunner 接口,会直接执行run方法。

我们在其中调用了WorkManager的startExecutor方法,用线程池execute方法启动了对应线程类的run方法。

test

package com.example.demospringboot;

import com.example.demospringboot.dao.UserMapper;
import com.example.demospringboot.bean.User;
import com.example.demospringboot.task.AsyncTasks;


import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.CompletableFuture;
import org.springframework.cache.CacheManager;

import java.util.List;

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
@Rollback(value = false)
public class DemospringbootApplicationTests {
    @Autowired()
    private UserMapper userMapper;

    @Autowired
    private CacheManager cacheManager;

    @Test
    public void testUserMapper() throws Exception {
        // deleteAllUsers
        userMapper.deleteAllUsers();

        // insertUser 插入2条
        User user = new User();
        user.setId(100);
        user.setUsername("Jacky");
        user.setPassword("1000");
        userMapper.insertUser(user);
        user.setId(200);
        user.setUsername("Mike");
        user.setPassword("2000");
        userMapper.insertUser(user);

        // findUserById
        user = userMapper.findUserById(100);
        Assert.assertEquals("Jacky", user.getUsername());

        // updateUserPassword
        user.setPassword("1500");
        userMapper.updateUserPassword(user);
        Assert.assertEquals("1500", user.getPassword());

        // deleteUserById
        userMapper.deleteUserById(100);

        // findAllUsers
        List<User> AllUsers = userMapper.findAllUsers();
        for (User u : AllUsers) {
            System.out.println(u);
        }
        //Assert.assertEquals(1, AllUsers.size());

        System.out.println("CacheManager type : " + cacheManager.getClass());

    }


    @Autowired
    private AsyncTasks asyncTasks;


    @Test
    public void testTasks() throws Exception {
        long start = System.currentTimeMillis();

        // 线程池1
        CompletableFuture<String> task1 = asyncTasks.doTaskOne("1");
        CompletableFuture<String> task2 = asyncTasks.doTaskOne("2");
        CompletableFuture<String> task3 = asyncTasks.doTaskOne("3");

        // 线程池2
        CompletableFuture<String> task4 = asyncTasks.doTaskTwo("4");
        CompletableFuture<String> task5 = asyncTasks.doTaskTwo("5");
        CompletableFuture<String> task6 = asyncTasks.doTaskTwo("6");

        // 一起执行
        CompletableFuture.allOf(task1, task2, task3, task4, task5, task6).join();

        long end = System.currentTimeMillis();
        log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
    }

}
相关推荐
茶馆大橘几秒前
【黑马点评】已解决java.lang.NullPointerException异常
java·开发语言
星辰@Sea4 分钟前
服务注册中心对比及使用场景分析
java·云原生
马剑威(威哥爱编程)8 分钟前
除了递归算法,要如何优化实现文件搜索功能
java·开发语言·算法·递归算法·威哥爱编程·memoization
bug菌¹10 分钟前
滚雪球学SpringCloud[4.1讲]: Spring Cloud Gateway详解
java·spring cloud·微服务
MuseLss30 分钟前
HashMap高频面试知识点
java·开发语言·哈希算法
tyler-泰勒32 分钟前
初始c++:入门基础(完结)
java·开发语言·c++
重生之我要进大厂1 小时前
LeetCode 876
java·开发语言·数据结构·算法·leetcode
_祝你今天愉快1 小时前
技术成神之路:设计模式(十四)享元模式
java·设计模式
小筱在线2 小时前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal2 小时前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot