线程池讲解及SpringBoot配置线程池&定时任务

一、线程池讲解

1. 线程池执行过程

  1. 新的线程请求进来时,会先判断核心线程数是否已满,如果未满则直接新建线程并执行,执行完将其放回线程池;
  2. 如果已满就再检查队列是否已满,如果没满就将当前线程请求加入阻塞队列,等待空闲线程分配;
  3. 如果已满就再检查线程池当前存在的线程数是否已达到规定的最大值,如果没有达到就创建线程执行;
  4. 如果达到就执行对应的饱和策略。

2. 主要参数

  1. corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;
  2. maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;
  3. keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;
  4. unit:keepAliveTime的单位
  5. workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
  6. threadFactory:线程工厂,用于创建线程,一般用默认即可;
  7. handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务;

3. 队列选择

它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列;

  1. 直接提交队列:设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,没执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。
new SynchronousQueue<Runnable>()
  1. 有界的任务队列:有界的任务队列可以使用ArrayBlockingQueue实现
new ArrayBlockingQueue<Runnable>(10)
  1. 无界的任务队列或有界任务队列可以使用LinkedBlockingQueue实现
new LinkedBlockingQueue<Runnable>(),
  1. 优先任务队列:优先任务队列通过PriorityBlockingQueue实现,会按照线程的优先级进行了重新排列执行。
new PriorityBlockingQueue<Runnable>()

4. 拒绝策略

  1. AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;
  2. CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;
  3. DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;
  4. DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;

二、线程池使用

1.SpringBoot @Async异步任务

如果没有配置线程池,使用@Async注解、默认会使用一个最大线程数为20的线程池。

@Component
@Async
public class AsyncTask {
	public Future<String> task4() throws InterruptedException{
		long begin = System.currentTimeMillis();
		Thread.sleep(2000L);
		long end = System.currentTimeMillis();
		System.out.println("任务4耗时="+(end-begin));
		return new AsyncResult<String>("任务4");
	}
	
	
	public Future<String> task5() throws InterruptedException{
		long begin = System.currentTimeMillis();
		Thread.sleep(3000L);
		long end = System.currentTimeMillis();
		System.out.println("任务5耗时="+(end-begin));
		return new AsyncResult<String>("任务5");
	}
	
	public Future<String> task6() throws InterruptedException{
		long begin = System.currentTimeMillis();
		Thread.sleep(1000L);
		long end = System.currentTimeMillis();
		System.out.println("任务6耗时="+(end-begin));
		return new AsyncResult<String>("任务6");
	}
}

2.使用

@RestController
@RequestMapping("/AsyTask")
public class TestAsyTask {

    @Autowired
    private AsyncTask task;

    @GetMapping("/Task")
    public String exeTask() throws InterruptedException{
        long begin = System.currentTimeMillis();


        Future<String> task4 = task.task4();
        Future<String> task5 = task.task5();
        Future<String> task6 = task.task6();
        //for(;;){
         //    if (task4.isDone() && task5.isDone() && task6.isDone()) {
         //        break;
        //  }
        // }
        String s1 = task4 .get();
        String s2 = task5 .get();
        String s3 = task6 .get();
        long end = System.currentTimeMillis();

        long total = end-begin;
        System.out.println("执行总耗时="+total);
        return "success";
    }
}
  1. 启动类
@EnableAsync

2. 使用Spring的线程池

Spring的线程池就是封装的java的ThreadPoolExecutor。

  1. 注入spring
@Bean(value = "springexecutors")
    public Executor springexecutors(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数
        executor.setCorePoolSize(5);
        //最大线程数
        executor.setMaxPoolSize(10);
        //线程保持时间 秒
        executor.setKeepAliveSeconds(3);
        //队列的存储个数
        executor.setQueueCapacity(10);
        //拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
  1. 引用
@Resource(name = "springexecutors")
    Executor springexecutors;
  1. 使用
springexecutors.execute(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
});
public Future<String> getData2() {
    return springexecutors.submit(() -> {
        TimeUnit.SECONDS.sleep(5);
        System.out.println("Callable+" + Thread.currentThread().getName());
        return "成功!";
    });
}
  1. 注解使用
@Component
@Async(value = "springexecutors")
public class AsyncTask {
	public void task(int i) throws InterruptedException{
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(i +":"+ Thread.currentThread().getName());
	}
}

3. 使用Java的线程池

  1. 注入spring
@Bean(value = "javaexecutor")
    public Executor javaexecutor(){
        ThreadPoolExecutor javaexecutor = new ThreadPoolExecutor(
                5, //核心线程数
                10, //最大线程数
                3, //线程保持时间
                TimeUnit.SECONDS, //线程保持时间单位
                new LinkedBlockingDeque<>(10), //线程缓存队列
                new ThreadPoolExecutor.CallerRunsPolicy()); //拒绝策略
        return javaexecutor;
    }
  1. 引用
@Resource(name = "javaexecutor")
    Executor javaexecutor;
  1. 使用
javaexecutor.execute(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
});

@Component
@Async(value = "javaexecutor")
public class AsyncTask {
	public void task(int i) throws InterruptedException{
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(i +":"+ Thread.currentThread().getName());
	}
}

三、定时任务

1. spring的定时任务

@Component
@EnableScheduling
@EnableAsync
public class ScheduleTask {
    @Async
    @Scheduled(fixedDelay = 1000*60*60*24*3)
     //@Scheduled(cron = "0/5 * * * * *")
    public void first() throws InterruptedException {
        String path = "";
    }
}

2. Quartz定时任务

  1. 介绍
    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:
    持久性作业 - 就是保持调度定时的状态;
    作业管理 - 对调度作业进行有效的管理;
  2. 集成,pom
<!-- quartz -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.2.1</version>
		</dependency>
  1. 任务
public class MyJob implements Job {

	public void execute(JobExecutionContext context) throws JobExecutionException {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
		String d = simpleDateFormat.format(new Date());
		System.out.println("quartz MyJob date:" + d);
	}

}
  1. 使用
public class Demo {
  public static void main(String[] args) throws SchedulerException {
	  //1.创建Scheduler的工厂
      SchedulerFactory sf = new StdSchedulerFactory();
      //2.从工厂中获取调度器实例
      Scheduler scheduler = sf.getScheduler();


      //3.创建JobDetail
      JobDetail jb = JobBuilder.newJob(MyJob.class)
              .withDescription("this is a ram job") //job的描述
              .withIdentity("ramJob", "ramGroup") //job 的name和group
              .build();

      //任务运行的时间,SimpleSchedle类型触发器有效
      long time=  System.currentTimeMillis() + 3*1000L; //3秒后启动任务
      Date statTime = new Date(time);

      //4.创建Trigger
          //使用SimpleScheduleBuilder或者CronScheduleBuilder
      Trigger t = TriggerBuilder.newTrigger()
                  .withDescription("")
                  .withIdentity("ramTrigger", "ramTriggerGroup")
                  //.withSchedule(SimpleScheduleBuilder.simpleSchedule())
                  .startAt(statTime)  //默认当前时间启动
                  .withSchedule(CronScheduleBuilder.cronSchedule("0 6,7 * * * ?")) //两秒执行一次
                  .build();

      //5.注册任务和定时器
      scheduler.scheduleJob(jb, t);

      //6.启动 调度器
      scheduler.start();
	}
}

3. Cron表达式