多线程(五) -- 并发工具(一) -- 线程池(三) -- Tomcat线程池、Frok/Join线程池

接上篇:ThreadPoolExecutor相关内容

7. Tomcat线程池

7.1 Tomcat主要分为两个部分:

  1. 连接器部分:Connnector(NIO EndPoint),负责对外沟通
  2. 容器部分:Container,负责实现servlet规范,去运行一些servlet组件

7.2 Tomcat 在哪里用到了线程池呢:连接器部分中的worker

当浏览器向服务器发起一个请求的过程:首先到LimmitLatch
1594993035182

  • LimitLatch 用来限流,可以控制最大连接个数,类似 J.U.C 中的 Semaphore 后面再讲
  • Acceptor 死循环,校验有没有新的连接,只负责【接收新的 socket 连接】
  • Poller 死循环,只负责监听 socket channel 是否有【可读的 I/O 事件】
  • 一旦可读,封装一个任务对象(socketProcessor),提交给 Executor 线程池处理
  • Executor 线程池中的工作线程最终负责【处理请求】

7.3 tomcat 线程池实现

Tomcat 线程池扩展了 ThreadPoolExecutor,行为稍有不同

  • 如果总线程数达到 maximumPoolSize,这时不会立刻抛 RejectedExecutionException 异常,而是再次尝试将任务放入队列,如果还失败,才抛出 RejectedExecutionException 异常

源码 tomcat-7.0.42:

// 覆盖父类的执行方法
public void execute(Runnable command, long timeout, TimeUnit unit) {
    submittedCount.incrementAndGet();
    try {
        super.execute(command);
        // 如果最大线程数到了,并且阻塞队列也满了,子类捉住异常并扩展
    } catch (RejectedExecutionException rx) {
        if (super.getQueue() instanceof TaskQueue) {
            final TaskQueue queue = (TaskQueue)super.getQueue();
            try {
                // 尝试任务重新进入阻塞队列
                if (!queue.force(command, timeout, unit)) {
                    submittedCount.decrementAndGet();
                    throw new RejectedExecutionException("Queue capacity is full.");
                }
            } catch (InterruptedException x) {
                submittedCount.decrementAndGet();
                Thread.interrupted();
                throw new RejectedExecutionException(x);
            }
        } else {
            submittedCount.decrementAndGet();
            throw rx;
        }
    }
}

TaskQueue中的任务插入阻塞队列方法:

 public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
    if ( parent.isShutdown() )
        throw new RejectedExecutionException(
                "Executor not running, can't force a command into the queue"
        );
    return super.offer(o,timeout,unit); //forces the item onto the queue, to be used if the task
    is rejected
}

7.4 Tomcat线程池相关配置

对应server.xml中相关配置:
在这里插入图片描述

7.4.1 Connector 配置

在这里插入图片描述

  • acceptorThreadCount:不用改,一个线程做建立连接的活就够了,建立连接很多时候都是处于阻塞状态,没有连接就在那边等着,所以不需要那么多现成
  • pollerThreadCount:不用改,采用多路复用的思想,一个线程就能监听多个channel
  • minSpareThreads:核心线程数
  • maxThreads:最大线程数
  • executor:如果定义了,就覆盖上面的核心及最大线程数,使用自己配置的
7.5 executor配置:

在这里插入图片描述

7.5 tomcat线程池工作方式:

tomcat线程池的工作方式做了一定修改:

注意:

  1. 这里的核心任务<核心线程时,任务是先加入队列中,线程再去队列中获取任务
  2. 当提交任务数大于核心线程,小于最大线程时,任务先加到队列中
  3. 当任务大于最大线程数,才创建救急线程
    在这里插入图片描述

8. Fork/Join线程池:

更多请看:https://blog.csdn.net/tyrroo/article/details/81390202

8.1 概念

Fork/Join 是 JDK 1.7 加入的新的线程池实现,它体现的是一种分治思想,适用于能够进行任务拆分的 cpu 密集型运算

所谓的任务拆分,是将一个大任务拆分为算法上相同的小任务,直至不能拆分可以直接求解。跟递归相关的一些计算,如归并排序、斐波那契数列、都可以用分治思想进行求解

Fork/Join 在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升了运算效率

Fork/Join 默认会创建与 cpu 核心数大小相同的线程池

8.2 使用

使用 提交给 Fork/Join 线程池的任务需要继承 RecursiveTask(有返回值)或 RecursiveAction(没有返回值),例如下 面定义了一个对 1~n 之间的整数求和的任务

@Slf4j(topic = "TestFork")
public class TestFork {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool(4);
        pool.invoke(new MyTask(5));
    }
}

// 1~n之间整数的和
@Slf4j(topic = "myTask")
class MyTask extends RecursiveTask<Integer> {
    private int n;

    public MyTask(int n) {
        this.n = n;
    }
    @Override
    public String toString() {
        return "{" + n + '}';
    }

    @SneakyThrows
    @Override
    protected Integer compute() {
        // 如果 n 已经为 1,可以求得结果了
        // 终止条件
        if (n == 1) {
            log.debug("join() {}", n);
            return 1;
        }

        // 将任务进行拆分(fork)
        MyTask t1 = new MyTask(n - 1);
        t1.fork(); // 拆分,让一个线程去执行此任务
        log.debug("fork() {} + {}", n, t1);

        // 合并(join)结果
        Thread.sleep(2000);
        int result = n + t1.join(); // 获取任务结果
        log.debug("join() {} + {} = {}", n, t1, result);
        return result;
    }
}

结果:

11:31:41.807 [ForkJoinPool-1-worker-1] DEBUG myTask - fork() 2 + {1}
11:31:41.807 [ForkJoinPool-1-worker-3] DEBUG myTask - fork() 5 + {4}
11:31:41.807 [ForkJoinPool-1-worker-5] DEBUG myTask - fork() 4 + {3}
11:31:41.807 [ForkJoinPool-1-worker-7] DEBUG myTask - fork() 3 + {2}
11:31:43.831 [ForkJoinPool-1-worker-7] DEBUG myTask - join() 1
11:31:43.833 [ForkJoinPool-1-worker-1] DEBUG myTask - join() 2 + {1} = 3
11:31:43.834 [ForkJoinPool-1-worker-7] DEBUG myTask - join() 3 + {2} = 6
11:31:43.834 [ForkJoinPool-1-worker-5] DEBUG myTask - join() 4 + {3} = 10
11:31:43.835 [ForkJoinPool-1-worker-3] DEBUG myTask - join() 5 + {4} = 15

用图表示:
在这里插入图片描述

8.3 改进

从上述图中可以看出,任务1等待任务2的结果,任务2等待任务3的结果,所以还是一个串行化的操作,所以我们需要拆分他们的依赖,改为尽量并行执行:

拆为2个任务:修改task类:

@Slf4j(topic = "myTask2")
class MyTask2 extends RecursiveTask<Integer> {

    int begin;
    int end;
    public MyTask2(int begin, int end) {
        this.begin = begin;
        new ReentrantLock();
        this.end = end;
    }
    @Override
    public String toString() {
        return "{" + begin + "," + end + '}';
    }
    @Override
    protected Integer compute() {
        // 5, 5
        if (begin == end) {
            log.debug("join() {}", begin);
            return begin;
        }
        // 4, 5
        if (end - begin == 1) {
            log.debug("join() {} + {} = {}", begin, end, end + begin);
            return end + begin;
        }

        // 1 5
        int mid = (end + begin) / 2; // 3
        MyTask2 t1 = new MyTask2(begin, mid); // 1,3
        t1.fork();
        MyTask2 t2 = new MyTask2(mid + 1, end); // 4,5
        t2.fork();
        log.debug("fork() {} + {} = ?", t1, t2);
        int result = t1.join() + t2.join();
        log.debug("join() {} + {} = {}", t1, t2, result);
        return result;
    }
}

结果:

11:45:21.203 [ForkJoinPool-1-worker-5] DEBUG myTask2 - fork() {1,2} + {3,3} = ?
11:45:21.203 [ForkJoinPool-1-worker-1] DEBUG myTask2 - join() 1 + 2 = 3
11:45:21.203 [ForkJoinPool-1-worker-3] DEBUG myTask2 - fork() {1,3} + {4,5} = ?
11:45:21.203 [ForkJoinPool-1-worker-7] DEBUG myTask2 - join() 4 + 5 = 9
11:45:21.210 [ForkJoinPool-1-worker-1] DEBUG myTask2 - join() 3
11:45:21.210 [ForkJoinPool-1-worker-5] DEBUG myTask2 - join() {1,2} + {3,3} = 6
11:45:21.210 [ForkJoinPool-1-worker-3] DEBUG myTask2 - join() {1,3} + {4,5} = 15

图片展示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值