Java线程池学习笔记

Spring的ThreadPoolTaskExecutor:
ThreadPoolTaskExecutor是借助JDK并发包中的ThreadPoolExecutor,类ThreadPoolTaskExecutor中包含ThreadPoolExecutor。

</pre><pre code_snippet_id="1576841" snippet_file_name="blog_20160211_1_1277057" name="code" class="java"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"
       default-lazy-init="false">

    <!-- callerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
    <bean id="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy"/>

    <!-- 线程池配置 -->
    <bean id="asynThreadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程组&名称 -->
        <property name="threadGroupName" value="epp-tg1"/>
        <property name="threadNamePrefix" value="remote-query-task"/>

        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="1"/>
        <!-- 线程池维护线程所允许的空闲时间,5分钟 -->
        <property name="keepAliveSeconds" value="300"/>
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="200"/>
        <!-- 线程池所使用的缓冲队列 -->
        <property name="queueCapacity" value="100"/>
        <!-- CallerRunsPolicy -->
        <property name="rejectedExecutionHandler" ref="callerRunsPolicy"/>
    </bean>

</beans>


(1)    线程池数量小于corePoolSize,则增加线程。即使线程池中的线程都处于空闲状态,也要创建新的线程来处理。

(2)    如果线程数量等于corePoolSize,则把请求放在workQueue中,池子里的线程空闲线程就去从workQueue中取任务并处理。

(3)    如果workQueue已满,则新建线程加入线程池,并处理请求,如果池子的大小撑到了maxPoolsize,就用rejectedExecutionHangdler 指定的策略来处理此任务。

(4)    如果当线程数大于corePoolsize的时候,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁。(线程数小于corePoolSize时,线程不会自动销毁,除非手动调用对应的方法)

总结:

处理任务的优先级为:核心线程corePoolSize、任务队列workQueue,最大线程maxPoolSize,如果三者都满了,使用handle处理被拒绝的任务。


测试:

Java配置:

<bean id="asynThreadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程组&名称 -->
        <property name="threadGroupName" value="epp-tg1"/>
        <property name="threadNamePrefix" value="remote-query-task"/>

        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="1"/>
        <!-- 线程池维护线程所允许的空闲时间,5分钟 -->
        <property name="keepAliveSeconds" value="1"/>
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="100"/>
        <!-- 线程池所使用的缓冲队列 -->
        <property name="queueCapacity" value="3"/>
        <!-- CallerRunsPolicy -->
        <property name="rejectedExecutionHandler" ref="callerRunsPolicy"/>
    </bean>


代码:

ApplicationContext applicationContext =new ClassPathXmlApplicationContext("multiThread/spring-biz-threadpools.xml");
        ThreadPoolTaskExecutor asynThreadPool=(ThreadPoolTaskExecutor) applicationContext.getBean("asynThreadPool");
        List<String> result =new ArrayList<>();
        List<Future<String>> list = new ArrayList<>();
        for (int i=0;i<10;++i){
            Future<String> future = asynThreadPool.submit(()->methodTest());
            System.out.println("活跃线程数:"+Thread.activeCount());
            list.add(future);
        }


运行结果:

活跃线程数:3
活跃线程数:3
活跃线程数:3
活跃线程数:3
活跃线程数:4
活跃线程数:5
活跃线程数:6

备注:活跃线程数包含主线程。

任务1,新建线程,当前活跃线程数为2;

任务2,corePoolSize已满,所以会将任务放在workQueue中,当前活跃线程数为2,

任务3,corePoolSize已满,所以会将任务放在workQueue中,当前活跃线程数为2,

任务4:corePoolSize已满,所以会将任务放在workQueue中,当前活跃线程数为2,

任务5:corePoolSize已满,workQueue已满,还未达到maxPoolSize,所以会新建线程,

.......




java代码:

<pre name="code" class="java"> @Test
    public void  test() throws ExecutionException, InterruptedException {
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("multiThread/spring-biz-threadpools.xml");
        ThreadPoolTaskExecutor asynThreadPool=(ThreadPoolTaskExecutor) applicationContext.getBean("asynThreadPool");
        List<String> result =new ArrayList<>();
        List<Future<String>> list = new ArrayList<>();
        for (int i=0;i<10;++i){
            Future<String> future = asynThreadPool.submit(()->methodTest());
            System.out.println("活跃线程数:"+Thread.activeCount());
            list.add(future);
        }

        for(Future<String> future:list){
            try{
                System.out.println(future.get());
            }catch (Exception e){
                e.printStackTrace();
            }
        }


        Thread.sleep(100000);


        System.out.println("test");

    }

    public String methodTest() throws Exception {
       System.out.println("thread test "+i );
       Thread.sleep(10000);
        return "call()方法被自动调用,线程名称:"  + Thread.currentThread().getName();
    }



 


注意:

如果线程抛出异常,在调用future.get()方法时,会将异常抛出到外部,抛出的异常为ExecutionException。在调用future.get()方法时,会等待线程运行完成,并取得返回结果。

通过e.getCause取出导致抛出异常的原因。


submit和execute的区别:

(1) 接受的参数不一样

submit接受的参数:接受runable或者Callable对象

execute接收的参数:接收runnable对象

(2) submit有返回值,execute无返回值

submit的返回值未Future<T> ,通过future类的get方法获取到返回值

(3) submit处理exception的方式和execute不同

submit处理异常:如果某个线程抛出异常,则通过future.get()获取到该异常

execute() 无法返回异常,run方法不能抛出异常,所以方法内部必须捕获异常。


runnable和callable的差别:

(1) Callable规定的方法是call(), Runnable规定的方法是run

(2) Callable的任务执行后可返回值,而runnable的认识是不能返回值的

(3) call方法可以抛出异常,run方法不可以

(4)运行Callable任务可以拿到一个Future对象,Future表示异步计算的结果,可通过get()方法取出返回值,如果线程出现异常,Future.get()会抛出InterruptedException或者ExecutionException,如果线程已取消,会抛出CancellationException。


线程容量已经达到饱和时,对于再到达的线程的处理策略:

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其RejectedExecutionHandler 的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:
(1)在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时RejectedExecutionException。
(2) 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
(3) 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
(4) 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。


ThreadPoolExecutor类:




参考资料:

http://www.importnew.com/17633.html (对应的英文文章:https://blog.bramp.net/post/2015/12/17/the-importance-of-tuning-your-thread-pools/)

http://www.importnew.com/17820.html(主要讲讲解了java的ThreadPoolExecutor原理)





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值