四、应用缓存
使用spring应用缓存。使用方式:使用@EnableCache注解激活Spring的缓存功能,需要创建一个CacheManager来处理缓存。如使用一个内存缓存示例
package com.github.bjlhx15.gradle.demotest; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; @Configuration @EnableCaching public class CacheConfiguration { @Bean public CacheManager cacheManager(){ SimpleCacheManager simpleCacheManager=new SimpleCacheManager(); simpleCacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("searches"))); return simpleCacheManager; } }
其他实现如:EhCacheManager、GuavaCacheManager等
主要标记:
@CacheEvict:将会从缓存中移除一个条目
@CachePut:会将方法结果放到缓存中,而不会影响到方法调用本身。
@Caching:将缓存注解重新分组
@CacheConfig:指向不同的缓存配置
更多spring 应用缓存:https://www.cnblogs.com/bjlhx/category/1233985.html
五、分布式缓存
推荐使用redis,系列文章:https://www.cnblogs.com/bjlhx/category/1066467.html
spring使用也比较方便:https://www.cnblogs.com/bjlhx/category/1233985.html
六、异步方法-EnableAsync
在程序执行时候还有一个瓶颈,串行执行,可以通过使用不同线程类快速提升应用的速度。
要启用Spring的异步功能,必须要使用@EnableAsync注解。这样将会透明地使用java.util.concurrent.Executor来执行所有带有@Async注解的方法。
@Async所修饰的函数不要定义为static类型,这样异步调用不会生效
针对调用的Async,如果不做Future特殊处理,执行完调用方法会立即返回结果,如异步邮件发送,不会真的等邮件发送完毕才响应客户,如需等待可以使用Future阻塞处理。
6.1、原始使用
1、main方法增加@EnableAsync注解
@SpringBootApplication @EnableAsync public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); System.out.println("ThreadId:"+Thread.currentThread().getId()); } }
2、在所需方法增加@Async注解
@Component public class Task { @Async public void doTaskOne() throws Exception { for (int i = 0; i < 3; i++) { Thread.sleep(200); System.out.println("ThreadId:"+Thread.currentThread().getId()+":doTaskOne"); } } @Async public void doTaskTwo() throws Exception { for (int i = 0; i < 3; i++) { Thread.sleep(200); System.out.println("ThreadId:"+Thread.currentThread().getId()+":doTaskTwo"); } } @Async public void doTaskThree() throws Exception { for (int i = 0; i < 3; i++) { Thread.sleep(200); System.out.println("ThreadId:"+Thread.currentThread().getId()+":doTaskThree"); } } }
3、查看调用
@RestController public class TestAsyns { @Autowired private Task task; @RequestMapping("/testAsync") public ResponseEntity testAsync() throws Exception { task.doTaskOne(); task.doTaskTwo(); task.doTaskThree(); return ResponseEntity.ok("ok"); } }
上述方法依次调用三个方法。
如果去除@EnableAsync注解,输出如下:【可见是串行执行】
ThreadId:33:doTaskOne ThreadId:33:doTaskOne ThreadId:33:doTaskOne ThreadId:33:doTaskTwo ThreadId:33:doTaskTwo ThreadId:33:doTaskTwo ThreadId:33:doTaskThree ThreadId:33:doTaskThree ThreadId:33:doTaskThree
如果增加@EnableAsync注解,输出如下:【可见是并行执行】
ThreadId:56:doTaskThree ThreadId:55:doTaskTwo ThreadId:54:doTaskOne ThreadId:54:doTaskOne ThreadId:55:doTaskTwo ThreadId:56:doTaskThree ThreadId:54:doTaskOne ThreadId:56:doTaskThree ThreadId:55:doTaskTwo
6.2、自定义执行器使用异步
1、配置类
方式一、注入Bean方式
import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class ThreadConfig { // 执行需要依赖线程池,这里就来配置一个线程池 @Bean public Executor getExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } }
方式二、通过实现AsyncConfigurer接口,可以自定义默认的执行(executor)。新增如下配置类:
@Configuration public class AsyncConfiguration implements AsyncConfigurer { protected final Logger logger = LoggerFactory.getLogger(AsyncConfiguration.class); @Override public Executor getAsyncExecutor() { //做好不超过10个,这里写两个方便测试 return Executors.newFixedThreadPool(2); } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex,method,params)->logger.error("Uncaught async error",ex); } }
Executor的初始化配置,还有很多种,可以参看https://www.cnblogs.com/bjlhx/category/1086008.html
Spring 已经实现的异步线程池:
1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方
3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类
5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装
使用上述配置,能够确保在应用中,用来处理异步任务的线程不会超过10个。这对于web应用很重要,因为每个客户端都会有一个专用的线程。你所使用的线程越多,阻塞时间越长那么能够处理的客户端就会越少。
如果设置成两个,程序中有3个异步线程,也会只有两个运行,如下
ThreadId:55:doTaskTwo ThreadId:54:doTaskOne ThreadId:55:doTaskTwo ThreadId:54:doTaskOne ThreadId:55:doTaskTwo ThreadId:54:doTaskOne ThreadId:55:doTaskThree ThreadId:55:doTaskThree ThreadId:55:doTaskThree
2、使用
同上述一致。
6.3、异步返回处理
方式一、使用Future【FutureTask是默认实现】处理+轮询处理【jdk1.5产物,没有提供Callback机制,只能主动轮询,通过get去获取结果】【不推荐】
修改异步执行的方法
@Component public class TaskFutureDemo { @Async public Future<String> doTaskOne() throws Exception { for (int i = 0; i < 3; i++) { Thread.sleep(1000); System.out.println("ThreadId:"+Thread.currentThread().getId()+":doTaskOne"); } return new AsyncResult<>("doTaskOne"); } @Async public Future<String> doTaskTwo() throws Exception { for (int i = 0; i < 3; i++) { Thread.sleep(1000); System.out.println("ThreadId:"+Thread.currentThread().getId()+":doTaskTwo"); } return new AsyncResult<>("doTaskTwo"); } @Async public Future<String> doTaskThree() throws Exception { for (int i = 0; i < 3; i++) { Thread.sleep(1000); System.out.println("ThreadId:"+Thread.currentThread().getId()+":doTaskThree"); } return new AsyncResult<>("doTaskThree"); } }