【线程池】异步任务

一、业务场景

      最近在做项目时,遇到一个业务场景:由于手机浏览器直接加载大PDF文件可能会导致加载失败,因此当用户上传PDF文件到FastDFS的时候,需要将pdf原文件上传,并且按照pdf文件页数转化成对应的图片在上传到FastDFS上。因为PDF转图片是一个比较耗时的操作,因此需要用到异步任务。

二、分析

      首先想到的就是主线程去执行上传pdf原文件的操作,然后再开启一个线程来进行PDF转图片并上传fastdfs的操作,pdf原文件上传成功后即返回给用户上传成功的标志。

      如果每发起一次请求,开启一个线程,当并发请求多的时候会导致线程数量非常多。而线程是非常宝贵的资源,随着并发访问量的提升,会发生线程堆栈溢出,创建新线程失败等问题。因此可以考虑采用线程池的方式来避免这种问题。(小伙伴,有没有感觉很熟悉,是不是非常类似于网络编程中的BIO和伪异步IO)。

三、java中的线程池

       首先先谈一谈为什么要用线程池?

        其一,减少创建和销毁线程所消耗的资源。其二,就是将当前任务与主线程隔离,能实现和主线程的异步执行,需要注意的是,一味的开线程也不一定能提高性能,线程休眠也需要耗费内存资源,所以需要合理选择线程池的大小。

        我们的任务(Task)提交的线程池之后,又是按照什么样的规则去运行的呢?

        1、exector一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。

        2、exector一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将任务放到workQueue中等待执行。

        3、exector一个线程之后,如果线程池中的线程数已达到核心线程数但未超过非核心线程数,且workQueue已满,则开启一个非核心线程来执行任务。

        4、execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则按照Hanlder策略做对应的方案,拒绝、交给调用线程处理等。

                                       

                                       

       几种常见线程池:

        1、CachedThreadPool :用于并发执行大量短期的小任务,或者是负载较轻的服务器

        2、FixedThreadPool:需要限制当前线程数量,用于负载比较重的服务器。

        3、SingleThreadExecotrs:用于串行执行任务的场景,每个任务按顺序,不需要并发执行。

        4、ScheduledThreadPoolExecutor 用于需要多个后台线程执行周期任务,同时需要限制线程数量的场景

        自定义线程池:如果任务是CPU密集型(需要进行大量计算、处理),则应该配置尽量少的线程,比如CPU个数+1,这样可以避免每个线程都需要使用很长时间,但是有太多线程争抢资源的情况。如果任务是IO密集型(主要时间都在I/O,空闲时间比较多)则应该配置多一些线程,比如CPU个数的两倍,这样能够充分压榨CPU的性能。

        线程池框架图如下所示:

四、示例代码

       1、异步任务类

//模拟异步任务
public class ExecutorDemo {

    private ExecutorService executor= Executors.newFixedThreadPool(4);
    public void asynTask(){
        executor.submit(new Runnable() {
            @Override
            public void run() {

                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int sum=0;
                for (int i=0;i<1000;i++){
                    sum+=i;
                }
                System.out.println(sum);
                System.out.println("-------------asynTask end--------------");

            }


        });
    }
}

     2、主任务类

public class ExecutorDemoClient {
    public static void main(String[] args){
        ExecutorDemo e =new ExecutorDemo();
        e.asynTask();
        System.out.println("-------------main end--------------");
    }

 输出结果示例:

可以看到主线程已经结束了,然后异步任务线程才结束。

四、总结:

       通过线程池的方式可以解决异步的问题,但是呢,如果异步任务执行失败,可能会伴随着数据的丢失等。

猜你喜欢

转载自blog.csdn.net/qq_26545305/article/details/80375900