Spring Boot实践八--用户管理系统(下)
step3:多线程task
首先,实现两个UserService和AsyncUserService两个服务接口:
接口:
package com.example.demospringboot.service;public interface UserService {void checkUserStatus();
}
package com.example.demospringboot.service;public interface AsyncUserService {void checkUserStatus();
}
对应实现:
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 {@Autowiredprivate UserMapper userMapper;@Overridepublic void checkUserStatus() {List<User> AllUsers = userMapper.findAllUsers();for (User u : AllUsers) {// System.out.println(ThreadUtils.getThreadName() + ": " + u);log.info("{}", u);}};
}
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 {@Autowiredprivate AsyncTasks asyncTasks;@Overridepublic void checkUserStatus() {asyncTasks.doTaskOne("1");asyncTasks.doTaskOne("2");asyncTasks.doTaskOne("3");};
}
用到的task类如下:
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工具类如下:
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线程池@Beanpublic 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进行管理:
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);@Autowiredprivate 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;}@Overridepublic void run() {while (true) {userService.checkUserStatus();// sleep 1sThreadUtils.sleepUtil(1000L);}}}
}
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);@Autowiredprivate 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;}@Overridepublic void run() {while (true) {asyncUserService.checkUserStatus();// sleep 1sThreadUtils.sleepUtil(1000L);}}}
}
主类如下:
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 {@Autowiredprivate WorkManager workManager;@Autowiredprivate AsyncWorkManager asyncWorkManager;public static void main(String[] args) {SpringApplication.run(DemospringbootApplication.class, args);}@Overridepublic 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;@Autowiredprivate CacheManager cacheManager;@Testpublic void testUserMapper() throws Exception {// deleteAllUsersuserMapper.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);// findUserByIduser = userMapper.findUserById(100);Assert.assertEquals("Jacky", user.getUsername());// updateUserPassworduser.setPassword("1500");userMapper.updateUserPassword(user);Assert.assertEquals("1500", user.getPassword());// deleteUserByIduserMapper.deleteUserById(100);// findAllUsersList<User> AllUsers = userMapper.findAllUsers();for (User u : AllUsers) {System.out.println(u);}//Assert.assertEquals(1, AllUsers.size());System.out.println("CacheManager type : " + cacheManager.getClass());}@Autowiredprivate AsyncTasks asyncTasks;@Testpublic void testTasks() throws Exception {long start = System.currentTimeMillis();// 线程池1CompletableFuture<String> task1 = asyncTasks.doTaskOne("1");CompletableFuture<String> task2 = asyncTasks.doTaskOne("2");CompletableFuture<String> task3 = asyncTasks.doTaskOne("3");// 线程池2CompletableFuture<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) + "毫秒");}}