ThreadPoolTaskExecutor 使用和原理

为什么要用线程池?

服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。

每个请求对应一个线程(thread-per-request)方法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源(线程的生命周期!)。在一个JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。

线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

使用线程池的风险

虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足,并发错误,线程泄漏,请求过载。

有效使用线程池的准则

(1)不要对那些同步等待其它任务结果的任务排队。这可能会导致上面所描述的那种形式的死锁,在那种死锁中,所有线程都被一些任务所占用,这些任务依次等待排队任务的结果,而这些任务又无法执行,因为所有的线程都很忙。

(2)在为时间可能很长的操作使用合用的线程时要小心。如果程序必须等待诸如 I/O 完成这样的某个资源,那么请指定最长的等待时间,以及随后是失效还是将任务重新排队以便稍后执行。这样做保证了:通过将某个线程释放给某个可能成功完成的任务,从而将最终取得 某些 进展。

理解任务。要有效地调整线程池大小,您需要理解正在排队的任务以及它们正在做什么。它们是 CPU 限制的(CPU-bound)吗?它们是 I/O 限制的(I/O-bound)吗?您的答案将影响您如何调整应用程序。如果您有不同的任务类,这些类有着截然不同的特征,那么为不同任务类设置多个工作队 列可能会有意义,这样可以相应地调整每个池。

线程池的创建

我们可以通过java.util.concurrent.ThreadPoolExecutor来创建一个线程池。

常用构造方法为:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler)

  • corePoolSize: 线程池维护线程的最少数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime: 线程池维护线程所允许的空闲时间
  • unit: 线程池维护线程所允许的空闲时间的单位
  • workQueue: 线程池所使用的缓冲队列
  • handler: 线程池对拒绝任务的处理策略

处理任务的优先级

线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
线程数量达到了corePools,则将任务移入队列等待
队列已满,新建线程(非核心线程)执行任务
队列已满,总线程数又达到了maximumPoolSize,就会抛出异常

handler有四个选择

ThreadPoolExecutor.AbortPolicy() :抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy() : 重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy() : 抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy() : 抛弃当前的任务

ThreadPoolTaskExecutor

四个属性在org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor里面都是传递给了java.util.concurrent.ThreadPoolExecutor 所以基本上线程的创建、任务的提交、任务的执行、线程的销毁都是ThreadPoolExecutor来做的

ThreadPoolTaskExecutor 使用

pom.xml

<dependency> 
<groupId>org.springframework</groupId> 
<artifactId>spring-context-support</artifactId> 
<version>3.0.5.RELEASE</version> 
</dependency> 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">
<beans>

<bean id ="taskExecutor"  class ="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
    <property name ="corePoolSize" value ="5" /> <!--核心线程数 -->
    <property name ="keepAliveSeconds" value ="3000" /> <!-- 某线程空闲超过这个时间,就回收该线程 -->
    <property name ="maxPoolSize" value ="10" />     <!--最大线程数 -->
    <property name ="queueCapacity" value ="1000" />  <!-- 队列大小 -->
    <property name= "rejectedExecutionHandler" >
        <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
        <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
        <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
        <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
        <bean class = "java.util.concurrent.ThreadPoolExecutor$DiscardPolicy"   />
    </property>
</bean>

</beans>

SpringThread.java

public class SpringThread extends Thread{

    private int parameter;

    public SpringThread(int parameter){
        this.parameter = parameter;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":执行了..." + parameter);
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试代码

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.task.TaskExecutor;

public class App
{
    public static void main( String[] args )
    {
        //System.out.println( "Hello World!" );

        ApplicationContext appContext = new ClassPathXmlApplicationContext("file:applicationContext.xml");
        TaskExecutor executor = (TaskExecutor) appContext.getBean("taskExecutor");
        for (int i = 0; i < 10; i++) {
            SpringThread t = new SpringThread(i);
            executor.execute(t);
        }
        System.out.println("main process is finish .....");
    }
}

运行结果:

taskExecutor-1:执行了...0
taskExecutor-2:执行了...1
taskExecutor-3:执行了...2
taskExecutor-4:执行了...3
taskExecutor-5:执行了...4
main process is finish .....
taskExecutor-1:执行了...5
taskExecutor-3:执行了...7
taskExecutor-4:执行了...8
taskExecutor-2:执行了...6
taskExecutor-5:执行了...9

转载源:
(1)http://www.cnblogs.com/xinxindiandeng/p/6383311.html
(2)http://blog.csdn.net/benbendy1984/article/details/54932267

猜你喜欢

转载自blog.csdn.net/bsfz_2018/article/details/79579723
今日推荐