四、应用缓存

  使用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");
    }
}
View Code

相关文章:

  • 2021-07-04
  • 2022-12-23
  • 2021-09-06
  • 2021-12-17
  • 2021-12-19
  • 2022-12-23
  • 2019-08-04
  • 2021-07-06
猜你喜欢
  • 2021-05-19
  • 2022-12-23
  • 2021-11-27
  • 2021-12-17
  • 2021-12-26
相关资源
相似解决方案