返回顶部
首页 > 资讯 > 后端开发 > Python >对springtask和线程池的深入研究
  • 898
分享到

对springtask和线程池的深入研究

2024-04-02 19:04:59 898人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录spring task和线程池的研究1、如何实现spring task定时任务的配置2、task里面的一个job方法如何使用多线程,配置线程池spring 线程池配置默认线程池T

spring task和线程池的研究

最近因工作需求,研究了一下spring task定时任务,和线程池,有了一定收获,记录一下

涉及如下内容

1、如何实现spring task定时任务的配置

2、task里面的一个job方法如何使用多线程,配置线程池

如何配置等待子线程结束后,再结束主线程

1、如何实现spring task定时任务的配置

因工作需要,需要定时执行一个方法,通过相关比较后,发现spring自带的task 可以满足,配置简单

步骤

1)增加配置文件 ,在applicationContext-cfg.xml 主配置文件里面添加 相关task标签


<beans xmlns="Http://www.springframework.org/schema/beans" xmlns:task="http://www.springframework.org/schema/task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/task
       http://www.springframework.org/schema/task/spring-task-3.0.xsd
       http://www.springframework.org/schema/jee     
       http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

2)编写bean类和执行方法

编写jobService类,里面实现testjobThread方法,调用的spring注入过的action、service方法


@Component("jobService")
public class jobService
{
    private static Logger logger = Logger.getLogger(jobService.class); 
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    final CountDownLatch countDownLatch = new CountDownLatch(3); 
 
    
    public void testjobThread()
    {
        Date startdate = new Date();
        logger.info("DZFP_job_JOB 开始执行任务...,时间   " + startdate);
        try
        {
            DzfpAction.Dzfp_SendAll();
        }
        catch (Exception e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
            logger.error(StringUtil.grabExceptionMessage(e));
        }
        Date enddate = new Date();
        logger.info("DZFP_job_JOB 任务完成...时间  " + enddate + "   耗时   " + String.valueOf(enddate.getTime() - startdate.getTime()) + "毫秒");
    }

3)配置task相关配置文件,在文件applicationContext-cfg.xml 中增加下列内容

pool-size="5" 该参数主要解决,多个调度并行的问题,如下图5个task任务,建议设置3--5个调度

如果配置参数为 1,下面5个task任务会依次执行,如果一个时间超出,后面的任务一直在等待,影响业务


 <!-- 定时任务 -->
 <task:scheduler id="scheduler" pool-size="5" />
 <task:scheduled-tasks scheduler="scheduler">
  <!-- 每天7点到7点55, 每隔5分钟执行一次 "0 0/5 7 * * ?"-->
  <task:scheduled ref="jobService" method="DZFPgetInvoie_job" cron="0 0/30 * * * ?" />
  <task:scheduled ref="jobService" method="DZFPgetInvoie_hong_job" cron="0 0/30 * * * ?" />
         <task:scheduled ref="jobService" method="testjobThread" cron="0/5 * * * * ?" />
  <task:scheduled ref="jobService" method="hzgd_job" cron="0/30 * * * * ?" />
  <task:scheduled ref="jobService" method="alipay_pay_job" cron="0/30 * * * * ?" />
 </task:scheduled-tasks>

使用以上配置后,启动项目就可以定时执行testjobThread方法里面的业务了。

2、task里面的一个job方法如何使用多线程,配置线程池

经过测试,spring task里面的方法是被串行执行的,比如上面配置的方法 testjobThread方法,5秒执行一次,如果有一个执行过程时间过长,后面的一次调度一直等上次执行结束后,才会启动下一次调用。

也就是说spring task是会监控 执行方法的主线程,如果主线程未结束的话,下一次就不会执行。

根据业务需求,这个testjobThread里面的 业务,需要多线程执行 (批量抽取数据)

spring框架里面,推荐使用线程池

1)配置线程池

在applicationContext-cfg.xml文件中增加配置如下


    <!-- spring线程池-->           
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="5" />
        <!-- 线程池维护线程所允许的空闲时间,默认为60s  -->
        <property name="keepAliveSeconds" value="200" />
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="20" />
        <!-- 缓存队列最大长度 -->
        <property name="queueCapacity" value="20" />
        <!-- 对拒绝task的处理策略   线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者-->
        <property name="rejectedExecutionHandler">
        <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
            <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
            <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
            <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
        <property name="waitForTasksToCompleteOnShutdown" value="true" />
    </bean>

2)修改业务操作类为thread类,实现run()方法

添加计数器CountDownLatch ,控制子线程结束后,再结束主线程

注意对象实现@Scope("prototype"),用到了成员变量参数


package cn.hao24.action;
import java.util.Date;   
import java.util.concurrent.CountDownLatch; 
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
 
import cn.hao24.util.DateUtil;
import cn.hao24.util.SprinGContextUtils;
 
@Component("testThreadAction")
@Scope("prototype")
public class testThreadAction extends Thread
{ 
 
    private String Treadname;
    private CountDownLatch latch;    
    public testThreadAction(String Treadname,CountDownLatch latch){
        this.Treadname=Treadname;
        this.latch=latch;
    }    
    @Override
    public void run()
    {            
        try
        {
            //主业务方法
            for (int i = 0; i < 10; i++)
            {
                Thread current = Thread.currentThread();
                System.out.println("线程号:"+current.getId() +"--"+current.getName()+" --"+Treadname +":---runing--- "+i+"--"+DateUtil.fORMat(new Date(), "yyyyMMddHHmmss") );
                Thread.sleep(20000);
            }
        }
        catch (InterruptedException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            //设置实例 执行完毕
            latch.countDown();
        }              
    }
    public void setTreadname(String treadname)
    {
        Treadname = treadname;
    }  
    public void setLatch(CountDownLatch latch)
    {
        this.latch = latch;
    }     
}

2)修改job调度的方法为多线程,配置3个线程


package cn.hao24.job; 
import java.util.Date;
import java.util.concurrent.CountDownLatch; 
import javax.annotation.Resource; 
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
 
import cn.hao24.action.DzfpAction;
import cn.hao24.action.HzgdAction;
import cn.hao24.action.KJGorderjob;
import cn.hao24.action.testThreadAction;
import cn.hao24.service.ZFBService;
import cn.hao24.util.SpringContextUtils;
import cn.hao24.util.StringUtil;
 
@Component("jobService")
public class jobService
{
    private static Logger logger = Logger.getLogger(jobService.class); 
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    final CountDownLatch countDownLatch = new CountDownLatch(3); 
    public void testjobThread()
    {
        try
        {
            CountDownLatch latch=new CountDownLatch(3);  //java工具类,类似与计数器,主要实现子线程未结束钱,主线程一直等待
            testThreadAction test1 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test1",latch);
            testThreadAction test2 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test2",latch);
            testThreadAction test3 = (testThreadAction)SpringContextUtils.getBean("testThreadAction","test3",latch);
            taskExecutor.execute(test1);
            taskExecutor.execute(test2);
            taskExecutor.execute(test3);
            latch.await(); //子线程未结束前,一直等待
            //test1.run();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            logger.error(StringUtil.grabExceptionMessage(e));
        }
    }
}

执行效果如下:

虽然 testjobThread 5秒执行一次,但是因为使用到了 latch.await() latch.countDown();需要等子线程执行完毕,才会进行下一次job

子线程每次循环,会sleep 20秒,从下面结果看,3个线程 每隔20秒才打印一次。符合最终要求

线程号:29--taskExecutor-3 --test3:---runing--- 0--20170622145500
线程号:28--taskExecutor-2 --test2:---runing--- 0--20170622145500
线程号:27--taskExecutor-1 --test1:---runing--- 0--20170622145500
线程号:28--taskExecutor-2 --test2:---runing--- 1--20170622145520
线程号:27--taskExecutor-1 --test1:---runing--- 1--20170622145520
线程号:29--taskExecutor-3 --test3:---runing--- 1--20170622145520
线程号:29--taskExecutor-3 --test3:---runing--- 2--20170622145540
线程号:28--taskExecutor-2 --test2:---runing--- 2--20170622145540
线程号:27--taskExecutor-1 --test1:---runing--- 2--20170622145540

spring 线程池配置

默认线程池ThreadPoolTaskExecutor配置

配置核心参数

直接在application.properties中配置核心参数


spring.task.execution.pool.core-size=8
spring.task.execution.pool.max-size=12
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.queue-capacity=100000
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.thread-name-prefix=swy-task-

创建JavaBean注入


@Configuration
public class ExecutorConfig {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
    @Bean
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(5);
        //配置最大线程数
        executor.setMaxPoolSize(6);
        //配置队列大小
        executor.setQueueCapacity(99999);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("swy-task-");
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

在配置类,或入口类开启@EnableAsync注解


@SpringBootApplication
@EnableAsync
public class MultiThreadApplication {
 public static void main(String[] args) {
  SpringApplication.run(MultiThreadApplication.class, args);
 }
}

在Service层或Controller层的类或方法上添加@Async注解


@Async
public void doSomethingAsync(){
 logger.info("start executeAsync");
 try{
  Thread.sleep(5000);
 }catch(Exception e){
  e.printStackTrace();
 }
 logger.info("end executeAsync");
}

自定义线程池ThreadPoolTaskExecutor配置

继承ThreadPoolTaskExecutor创建新线程池类


public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    private static final Logger logger = LoggerFactory.getLogger(CustomThreadPoolTaskExecutor.class);
    private void showThreadPoolInfo(String prefix){
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
        if(null==threadPoolExecutor){
            return;
        }
        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }
    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }
    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }
    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }
    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }
    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

配置新建线程池类的核心参数


@Configuration
public class ExecutorConfig {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
    @Bean
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(5);
        //配置最大线程数
        executor.setMaxPoolSize(8);
        //配置队列大小
        executor.setQueueCapacity(99999);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

在配置类,或入口类开启@EnableAsync注解


@SpringBootApplication
@EnableAsync
public class MultiThreadApplication {
 public static void main(String[] args) {
  SpringApplication.run(MultiThreadApplication.class, args);
 }
}

在Service层或Controller层的类或方法上添加@Async注解,此时需需注意一定要注明Bean方法名称。


@Async("asyncServiceExecutor")
public void doSomethingAsync(){
 logger.info("start executeAsync");
 try{
  Thread.sleep(5000);
 }catch(Exception e){
  e.printStackTrace();
 }
 logger.info("end executeAsync");
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 对springtask和线程池的深入研究

本文链接: https://lsjlt.com/news/161164.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 对springtask和线程池的深入研究
    目录spring task和线程池的研究1、如何实现spring task定时任务的配置2、task里面的一个job方法如何使用多线程,配置线程池spring 线程池配置默认线程池T...
    99+
    2024-04-02
  • 对Keras自带Loss Function的深入研究
    本文研究Keras自带的几个常用的Loss Function。 1. categorical_crossentropy VS. sparse_categorical_crossent...
    99+
    2024-04-02
  • Java语言深入 多线程程序模型研究(转)
    Java语言深入 多线程程序模型研究(转)[@more@]多线程是较复杂程序设计过程中不可缺少的一部分。为了提高应用程序运行的性能,采用多线程的设计是一种比较可行的方案。本文通过介绍使用Java编写的扫描计算机端口的实例,来说明多线程设计中...
    99+
    2023-06-03
  • 深入研究:Sybase和Oracle数据库的技术对比
    Sybase和Oracle是两个常见的关系型数据库管理系统,它们在企业领域被广泛应用。本文将深入研究Sybase和Oracle数据库的技术对比,包括各自的优势、劣势和适用场景,并给出具...
    99+
    2024-03-08
    oracle 对比 sybase
  • 研究绝对定位概念和原理的深入分析
    绝对定位:一种精确控制元素位置的CSS属性 引言:在网页设计中,精确控制元素位置是非常重要的。而绝对定位是CSS中一种非常便捷的方法来实现这一目标。绝对定位可以让我们将元素从正常的文档流中脱离出来,并且以自定义的位置进行放置。本...
    99+
    2024-01-23
    原理 概念 绝对定位
  • 深入研究:go和golang的差异详解
    深入探究:Go 和 Golang 之间的差异随着现代技术的飞速发展和应用需求的不断增长,编程语言成为了开发者们必备的工具。近年来,Go(也被称为Golang)在编程界引起了广泛的关注和讨论。然而,对于初学者和有经验的开发者来说,Go 和 G...
    99+
    2023-12-29
    差异比较:go vs golang
  • Golang函数库的深入研究和分析
    go 函数库提供了丰富的内置函数,包括:fmt:用于格式化和打印数据;io:用于输入/输出操作;math:提供了数学函数和常量;net:用于网络连接和服务器功能;os:用于与操作系统交互...
    99+
    2024-04-19
    golang 函数库
  • 深入研究rowcount函数的功能
    深入理解rowcount函数的用法,需要具体代码示例在数据库操作中,经常会使用到rowcount函数。该函数用于获取上一个执行的SQL语句所影响的行数。深入理解rowcount函数的用法,有助于更好地掌握数据库操作。在Python中,我们可...
    99+
    2023-12-29
    深入理解 rowcount函数
  • 揭秘 Java JUnit 的奥妙:深入研究
    JUnit 的基础 JUnit 是一种基于注释的框架,它使用 @Test 注释来标识要执行的测试方法。这些方法必须不返回任何值,并且不能抛出任何受检异常。JUnit 会自动运行所有带 @Test 注释的方法,并报告任何失败或错误。 示例...
    99+
    2024-02-16
    Java JUnit 单元测试 测试自动化
  • 深入研究绝对值编码器定位程序的技术特征
    绝对值编码器定位程序是现代自动化控制系统中广泛应用的一种技术,在工业控制、机器人技术和精密机械等领域都有重要的应用。本文将探究绝对值编码器定位程序的技术特点,包括编码器工作原理、高精度定位、多轴同步等方面的特点。 首先,绝对值编...
    99+
    2024-01-18
    定位程序:定位
  • 深入研究 PHP 函数性能测试和基准
    通过基准测试,可以深入了解 php 函数性能:识别需要测试的函数。设置基准用例并定义待测函数输入和执行次数。使用基准工具(如 phpbench)进行测试并收集性能指标。比较结果,识别性能...
    99+
    2024-04-11
    php 函数性能测试
  • 怎么深入理解线程池
    本篇内容主要讲解“怎么深入理解线程池”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么深入理解线程池”吧!本文将会从以下几个方面来介绍线程池的原理。 为什么要用线程池 线程池...
    99+
    2023-06-15
  • Android Dispatchers.IO线程池深入刨析
    目录一. Dispatchers.IO1.Dispatchers.IO2.DefaultScheduler类3.LimitingDispatcher类4.ExperimentalCo...
    99+
    2024-04-02
  • JavaScript数组去重问题的深入研究
    目录前言👀开始研究🐱‍🏍原始🧶利用indexOf优化原始方法✍再次优化,filter方法...
    99+
    2024-04-02
  • Resty开发restful版本的Jfinal深入研究
    目录前言官方实例一、独有优点:二、运行EXAMPLE示例:前言 自发现了resty后,一直进行深入考究到深夜3点才睡,只想说这6个小时的体验博主内心是满足的!说resty是restf...
    99+
    2024-04-02
  • 深入研究MySQL的自动提交功能
    标题:深入研究MySQL的自动提交功能 在使用MySQL数据库进行开发和管理时,自动提交功能是一个非常重要的特性。它决定了每一条SQL语句在执行后是否立即提交事务,对于事务的原子性和数...
    99+
    2024-03-15
    mysql 自动提交 深入研究 sql语句
  • 404错误的原因和解决方法的深入研究
    探究HTTP状态码404的原因和解决途径 引言:在浏览网页的过程中,我们经常会遇到HTTP状态码404。这个状态码表示服务器未能找到请求的资源。在本文中,我们将探究HTTP状态码404...
    99+
    2024-02-25
    http 状态码 原因 解决途径
  • 深入研究:PHP、Unix、Django和Bash的优缺点是什么?
    PHP、Unix、Django和Bash是目前非常流行的编程语言和操作系统,它们各有优缺点。在本篇文章中,我们将深入研究这些编程语言和操作系统的特点,以便更好地理解它们的适用范围。 一、PHP PHP是一种服务器端脚本语言,用于动态Web页...
    99+
    2023-11-11
    unix django bash
  • ASP Performance Monitor: 深入研究你的网站背后
    ASP Performance Monitor的功能 ASP Performance Monitor提供了一系列功能,可以帮助您深入研究您的网站后台并改进性能。这些功能包括: 性能分析:您可以使用ASP Performance Moni...
    99+
    2024-02-05
    ASP Performance Monitor, Web性能, 网站性能优化, 性能分析, 请求追踪, 异常跟踪
  • Go语言不同版本的演变和改进:深入研究
    深入研究Go语言的不同版本间的变化和进步 Go语言作为一门相对年轻的编程语言,自问世以来一直备受开发者的青睐。它简洁、高效、并发能力强等特点使得它在云计算、大数据、微服务等领域发挥了巨大的作用。随着时间的推移...
    99+
    2024-01-20
    Go版本变化 Go语言进步
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作