Tomcat线程池与Fork/Join使用

  • 1 Tomcat线程池
  • 1 简介
  • 2 Connector 配置
  • 3 Executor 线程配置
  • 2 Fork/Join
  • 1 说明
  • 2 案例


1 Tomcat线程池

1 简介

线程池调用多个接口java 线程池怎么使用join_java

Tomcat使用线程池:

  • LimitLatch 用来限流,可以控制最大连接个数
  • Acceptor 只负责 接收新的 socket 连接
  • Poller 只负责监听 socket channel 是否有 可读的 I/O 事件
  • 一旦可读,封装一个任务对象(socketProcessor),提交给 Executor 线程池处理
  • Executor 线程池中的工作线程最终负责进行处理请求

Tomcat 线程池扩展了 ThreadPoolExecutor, 但是总线程数达到 maximumPoolSize, 此时不会立即抛出 RejectedExecutionException 异常, 而是再次尝试将任务放入队列,如果再次失败, 才抛出RejectedExecutionException 异常.

部分源码

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;
         }
     }
}

任务队列

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"
     );
     //forces the item onto the queue, to be used if the task is rejected
     return super.offer(o,timeout,unit); 
}

2 Connector 配置

配置项

默认值

说明

acceptorThreadCount

1

acceptor 线程数量

pollerThreadCount

1

poller 线程数量

minSpareThreads

10

核心线程数,即 corePoolSize

maxThreads

200

最大线程数,即 maximumPoolSize

executor

最大线程数,即 maximumPoolSize

3 Executor 线程配置

配置项

默认值

说明

threadPriority

5

线程优先级

daemon

true

是否守护线程

minSpareThreads

25

核心线程数,即 corePoolSize

maxThreads

200

最大线程数,即 maximumPoolSize

maxIdleTime

60000

线程生存时间,单位是毫秒,默认值即 1 分钟

maxQueueSize

Integer.MAX_VALUE

队列长度

prestartminSpareThreads

false

核心线程是否在服务器启动时启动

任务执行流程图:

线程池调用多个接口java 线程池怎么使用join_线程池_02

2 Fork/Join

1 说明

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

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

Fork/Join 在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,可提升了运算效率, 默认会创建与 cpu 核心数大小相同的线程池.

2 案例

提交给 Fork/Join 线程池的任务需要继承 RecursiveTask(有返回值)或 RecursiveAction(没有返回值)

创建了一个对 1~n 之间的整数求和的任务.

@Slf4j(topic = "c.AddTask")
class AddTask1 extends RecursiveTask<Integer> {
    
     int n;
     public AddTask1(int n) {
         this.n = n;
     }
     @Override
     public String toString() {
         return "{" + n + '}';
     }
    
     @Override
     protected Integer compute() {
         // 如果 n 已经为 1,可以求得结果了
         if (n == 1) {
         log.debug("join() {}", n);
         return n;
         }

         // 将任务进行拆分(fork)
         AddTask1 t1 = new AddTask1(n - 1);
         t1.fork();
         log.debug("fork() {} + {}", n, t1);

         // 合并(join)结果
         int result = n + t1.join();
         log.debug("join() {} + {} = {}", n, t1, result);
         return result;
     }
}

测试

public static void main(String[] args) {
     ForkJoinPool pool = new ForkJoinPool(4);
     System.out.println(pool.invoke(new AddTask1(5)));
}

运行结果

[ForkJoinPool-1-worker-0] - fork() 2 + {1} 
[ForkJoinPool-1-worker-1] - fork() 5 + {4} 
[ForkJoinPool-1-worker-0] - join() 1 
[ForkJoinPool-1-worker-0] - join() 2 + {1} = 3 
[ForkJoinPool-1-worker-2] - fork() 4 + {3} 
[ForkJoinPool-1-worker-3] - fork() 3 + {2} 
[ForkJoinPool-1-worker-3] - join() 3 + {2} = 6 
[ForkJoinPool-1-worker-2] - join() 4 + {3} = 10 
[ForkJoinPool-1-worker-1] - join() 5 + {4} = 15 
15

执行流程图:

线程池调用多个接口java 线程池怎么使用join_线程池调用多个接口java_03

方案优化

class AddTask3 extends RecursiveTask<Integer> {
 
     int begin;
     int end;
     public AddTask3(int begin, int end) {
         this.begin = begin;
         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
         // 3
         int mid = (end + begin) / 2; 
         // 1,3
         AddTask3 t1 = new AddTask3(begin, mid); 
         t1.fork();
         // 4,5
         AddTask3 t2 = new AddTask3(mid + 1, end); 
         t2.fork();
         log.debug("fork() {} + {} = ?", t1, t2);
         int result = t1.join() + t2.join();
         log.debug("join() {} + {} = {}", t1, t2, result);
         return result;
     }
}

测试

public static void main(String[] args) {
 ForkJoinPool pool = new ForkJoinPool(4);
 System.out.println(pool.invoke(new AddTask3(1, 10)));
}

运行结果

[ForkJoinPool-1-worker-0] - join() 1 + 2 = 3 
[ForkJoinPool-1-worker-3] - join() 4 + 5 = 9 
[ForkJoinPool-1-worker-0] - join() 3 
[ForkJoinPool-1-worker-1] - fork() {1,3} + {4,5} = ? 
[ForkJoinPool-1-worker-2] - fork() {1,2} + {3,3} = ? 
[ForkJoinPool-1-worker-2] - join() {1,2} + {3,3} = 6 
[ForkJoinPool-1-worker-1] - join() {1,3} + {4,5} = 15 
15

流程执行图:

线程池调用多个接口java 线程池怎么使用join_java_04