Executors 和 ThreadPoolExecutor 和 CompletableFuture 三者 区别

Java线程池:Executors与ThreadPoolExecutor对比
该文章已生成可运行项目,

一、Executors 和 ThreadPoolExecutor 的区别在于:

Executors是一个线程池的工具类,而ThreadPoolExecutor是Executor接口的一个实现,是线程池的核心类。‌ Executors提供了多种快速创建线程池的方法,而ThreadPoolExecutor则提供了更高的自定义和控制能力‌。

Executors是一个工具类,用于快速创建不同类型的线程池,如固定大小的线程池、可缓存的线程池和单线程池等。它通过构造ThreadPoolExecutor的不同参数实例来创建线程池,适用于不想深入线程池内部实现的场景。而ThreadPoolExecutor是Java并发包中提供的具体实现类,继承自Executor接口,提供了更细致的控制和配置选项,适用于需要高度自定义线程池行为的场景‌。

使用Executors创建线程池的优点是简单快捷,通过调用Executors提供的静态方法即可快速创建一个线程池,无需关心线程池的内部实现细节。缺点是缺乏灵活性,无法根据具体需求调整线程池的参数。而使用ThreadPoolExecutor创建线程池的优点是高度自定义,可以根据具体需求设置核心参数,如核心线程数、最大线程数、存活时间和工作队列等。缺点是需要更多的代码来实现,对于简单的需求来说可能过于复杂‌

二、Excutors 与 ThreadPoolExcutor 的关系结论:

线程池的创建分为两种:

  • Executors
  • ThreadPoolExecutor

1.0、Executors

Executors 创建线程池的方式有以下几种:

  • Executors.newFixedThreadPool:创建固定大小的线程池
  • Executors.newWorkStealingPool:创建抢占式线程池
  • Executors.newSingleThreadExecutor:创建单个线程的线程池
  • Executors.newCachedThreadPool:创建可缓存的线程池
  • Executors.newSingleThreadScheduledExecutor:创建单线程可执行延迟任务的线程池
  • Executros.newScheduledThreadPool:创建可执行延迟任务的线程池

public class Executors {
    /**
     * 创建可重用的固定数量的线程池。如果所有线程都处在活动状态,提交额外任务
     * 的时候,超出的线程在队列中等待。队列类型是 LinkedBlockingQueue
     * nThreads 为创建线程池的数量
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    /**
     * Java 8 新增的线程池,具有抢占式操作的线程池,每个线程都有一个任务队列存放任务,
     * 当线程发现自己的队列没有任务了,也就是先工作完了的线程就去帮助没处理完的线程工作。
     * 以实现最快完成工作。
     * 是基于 ForkJoinPool 创建的线程池,parallelism 参数自定义并行度。
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    /**
     * 同上,但是并行度是根据获取当前系统的 CPU 核心数来判断。
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    /**
     * 创建固定数量线程的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
     * 多了一个 ThreadFactory 参数,线程工厂,主要用来创建线程池
     */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

    /**
     * 创建单个线程的线程池。它可以保证先进先出的执行顺序。
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    /**
     * 同上。
     */
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

    /**
     * 创建一个可缓存的线程池。适用于短期执行的异步任务,从代码中可以看出线程数的设定是
     * Integer.MAX_VALUE,不对线程池的大小做设定,可以创建无限数量的线程。
     * 但也就不适合执行长时间运行的任务,可能会导致系统资源耗尽。
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    /**
     * 同上
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

    /**
     * 创建一个单线程的可以执行延迟任务的线程池,可以安排以给定延迟后执行的命令或定时执行的命令。
     * 
     */
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

    /**
     * 同上
     */
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }

    /**
     * 创建一个可以执行延迟任务的线程池,任务数量自定义
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    /**
     * 同上
     */
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
}
 

1.1、ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize, // 核心线程数,线程池中始终存活的线程数
                           // 最大线程数,线程池中允许的最大线程数
                          int maximumPoolSize,
                          // 最大线程数可以存活的时间
                          long keepAliveTime, 
                          // 时间单位
                          TimeUnit unit, 
                          // 阻塞队列,workQueue 包含七种
                          BlockingQueue<Runnable> workQueue, 
                           // 线程工厂,主要用来创建线程
                          ThreadFactory threadFactory,
                          // 拒绝策略
                          RejectedExecutionHandler handler ) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    this.prestartAllCoreThreads();
}

三、ThreadPoolExecutor的重要参数

1、corePoolSize:核心线程数

1     * 核心线程会一直存活,及时没有任务需要执行
2     * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
3     * 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

2、maxPoolSize:最大线程数

1     * 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
2     * 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

3、 keepAliveTime:线程空闲时间

1     * 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
2     * 如果allowCoreThreadTimeout=true,则会直到线程数量=0

4、allowCoreThreadTimeout:允许核心线程超时

5、queueCapacity:任务队列容量(阻塞队列)

    * 当核心线程数达到最大时,新任务会放在队列中排队等待执行

6、rejectedExecutionHandler:任务拒绝处理器

 1     * 两种情况会拒绝处理任务:
 2         - 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
 3         - 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
 4     * 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
 5     * ThreadPoolExecutor类有几个内部实现类来处理这类情况:
 6         - AbortPolicy 丢弃任务,抛运行时异常
 7         - CallerRunsPolicy 执行任务
 8         - DiscardPolicy 忽视,什么都不会发生
 9         - DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
10     * 实现RejectedExecutionHandler接口,可自定义处理器
  • 拒绝策略

rejectedExectutionHandler参数字段用于配置绝策略,常用拒绝策略如下

AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException

CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。

DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。

DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。

四、ThreadPoolExecutor执行顺序

线程池按以下行为执行任务

1 1. 当线程数小于核心线程数时,创建线程。
2 2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
3 3. 当线程数大于等于核心线程数,且任务队列已满
4     - 若线程数小于最大线程数,创建线程
5     - 若线程数等于最大线程数,抛出异常,拒绝任务

五、如何设置参数

计算公式分为:

CPU密集型(CPU-bound)

CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading 很高。
在多重程序系统中,大部分时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部分时间用在三角函数和开根号的计算,便是属于CPU bound的程序。
CPU bound的程序一般而言CPU占用率相当高。这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。
线程数一般设置为:
线程数 = CPU核数+1 (现代CPU支持超线程)

IO密集型(I/O bound)

IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。
I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力
线程数一般设置为:
线程数 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

1、默认值

  • corePoolSize=1
  • maxPoolSize=Integer.MAX_VALUE
  • keepAliveTime=60s
  • allowCoreThreadTimeout=false
  • queueCapacity=Integer.MAX_VALUE
  • rejectedExecutionHandler=AbortPolicy()
  • 需要根据几个值来决定
    •   tasks :每秒的任务数
    •   tasktime:每个任务花费时间
    •   responsetime:系统允许容忍的最大响应时间,比如每个任务的响应时间不得超过2秒。

2、默认值
corePoolSize:
每个任务需要tasktime秒处理,则每个线程每钞可处理1/tasktime个任务。系统每秒有tasks个任务需要处理,则需要的线程数为:tasks/(1/tasktime),即taskstasktime个线程数。假设系统每秒任务数为100 -1000,每个任务耗时0.1秒,则需要1000.1至1000*0.1,即10~100个线程。那么corePoolSize应该设置为大于10,具体数字最好根据8020原则,即80%情况下系统每秒任务数,若系统80%的情况下第秒任务数小于200,最多时为1000,则corePoolSize可设置为20。

queueCapacity:
任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)*responsetime: (20/0.1)*2=400,即队列长度可设置为400。
队列长度设置过大,会导致任务响应时间过长,切忌以下写法:
LinkedBlockingQueue queue = new LinkedBlockingQueue();
这实际上是将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也将随之陡增。

关于网上说这个公式有问题,应该是(corePoolSize/tasktime)*responsetime-corePoolSize。400-20=380,不然队列的最后20个会超时。(PS:还没有测试过)

maxPoolSize:
当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200),即60个线程,可将maxPoolSize设置为60。

keepAliveTime:
线程数量只增加不减少也不行。当负载降低时,可减少线程数量,如果一个线程空闲时间达到keepAliveTiime,该线程就退出。默认情况下线程池最少会保持corePoolSize个线程。

allowCoreThreadTimeout:
默认情况下核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。

以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况,比如,当线程数量达到50时,CPU达到100%,则将maxPoolSize设置为60也不合适,此时若系统负载长时间维持在每秒1000个任务,则超出线程池处理能力,应设法降低每个任务的处理时间(tasktime)。    

总结基于实践有以下几个配置公式:
1、corePoolSize = 每秒需要多少个线程处理=tasks/(1/tasktime) =tasks*tasktime
2、queueCapacity = (coreSizePool/tasktime)*responsetime
3、maxPoolSize = (max(tasks)- queueCapacity)/(1/tasktime)

附:
关于queueCapacity任务队列java中提供了四种方式:直接提交队列、有界任务队列、无界任务队列、优先任务队列
1.SynchronousQueue
使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的进程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略;

2.ArrayBlockingQueue
使用ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。

3.LinkedBlockingQueue
使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。

4.PriorityBlockingQueue
PriorityBlockingQueue它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

————————————————

————————————————

————————————————

六、CompletableFuture 和 ThreadPoolExecutor 区别


CompletableFuture 和ThreadPoolExecutor 都是异步处理,但是在使用场景上还是有很大差异的,CompletableFuture 是偏异步处理操作,ThreadPoolExecutor 偏处理计算和大量请求的场景
 
1、功能和用途:

1)、CompletableFuture:CompletableFuture是用于异步编程的工具,它提供了一种方便的方式来处理异步任务的结果。它支持链式操作,可以将多个异步任务组合和协调起来,实现更复杂的异步处理逻辑。CompletableFuture适用于需要对多个异步任务进行组合和协调的场景。

ThreadPoolExecutor:ThreadPoolExecutor是Java中的一个线程池实现,用于管理和调度多个线程执行任务。它可以控制并发线程的数量,避免过多的线程创建和销毁开销,提高任务的执行效率。ThreadPoolExecutor适用于处理大量的计算密集型任务或IO密集型任务,通过线程池来管理和复用线程,提高系统的并发处理能力。

编程模型:

2)、CompletableFuture:CompletableFuture是基于Future和Promise模型的扩展,它提供了更丰富的异步编程功能。它允许使用回调函数、函数式操作和异常处理等高级编程模式,可以更方便地处理异步任务的结果和异常情况。

ThreadPoolExecutor:ThreadPoolExecutor是一个底层的线程池实现,它主要关注线程的管理和任务的调度执行。它并没有提供特定的编程模型,需要手动管理任务的提交和线程的调度。

灵活性:

3)、CompletableFuture:CompletableFuture提供了更灵活的方式来处理异步任务。它可以通过各种方法和组合操作来创建和组织异步任务,支持串行、并行、汇聚等不同的操作模式。

ThreadPoolExecutor:ThreadPoolExecutor相对而言更为底层,需要手动管理任务的提交和线程的调度。它可以手动设置线程池的核心线程数、最大线程数、任务队列等参数,提供更精细的控制和调整。

4)、综上所述,CompletableFuture适用于更复杂的异步处理逻辑,支持高级编程模式和操作,而ThreadPoolExecutor适用于更基本的多线程任务管理和调度。根据具体的需求和场景,选择合适的工具来实现异步编程和处理多线程任务。在一些情况下,CompletableFuture和ThreadPoolExecutor可以结合使用,以充分发挥它们各自的优势。

2、什么是 CompletableFuture

在Java中CompletableFuture用于异步编程,异步通常意味着非阻塞,运行任务单独的线程,与主线程隔离。并且通过回调可以在主线程中得到异步任务的执行状态,是否完成和异常等信息。

通过这种方式,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。使用这种并行方式,可以极大的提高程序的性能。

3、应用场景

(1)、描述依赖关系

thenApply() 把前面异步任务的结果,交给后面的Function

thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回

(2)、描述and聚合关系

thenCombine 任务合并,有返回值

thenAccepetBoth 两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值

runAfterBoth 两个任务都执行完成后,执行下一步操作(Runnable)

(3)、描述or聚合关系

applyToEither 两个任务谁执行的快,就使用那一个结果,有返回值

acceptEither 两个任务谁执行的快,就消耗那一个结果,无返回值

runAfterEither 任意一个任务执行完成,进行下一步操作(Runnable)

(4)、并行执行

CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行

(5)、静态方法创建
/**
supply 和 run 的主要区别就是 supply 可以有返回值,run 没有返回值。
Executor 就是用来执行异步任务的线程池,
如果不传Executor 的话,默认是ForkJoinPool这个线程池的实现。
一旦通过静态方法来构造,会立马开启异步线程执行Supplier或者Runnable提交的任务。
**/
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

(6)、获取任务执行结果

public T get();

public T get(long timeout, TimeUnit unit);

public T getNow(T valueIfAbsent);

public T join();

get()和get(long timeout, TimeUnit unit)是实现了Future接口的功能,两者主要区别就是get()会一直阻塞直到获取到结果;

get(long timeout, TimeUnit unit)值可以指定超时时间,当到了指定的时间还未获取到任务,就会抛出TimeoutException异常。

getNow(T valueIfAbsent):就是获取任务的执行结果,但不会产生阻塞。如果任务还没执行完成,那么就会返回你传入的valueIfAbsent 参数值,如果执行完成了,就会返回任务执行的结果。

join():跟get()的主要区别就是,get()会抛出检查时异常,join()不会。
 

————————————————————————————————————

————————————————————————————————————

————————————————————————————————————

示例:

import com.alibaba.fastjson2.JSON;


import com.test.DingDingSendMessage;
import com.test.WaybillWarnMsgQueue;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.*;

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class MsgqueueTest {


    @Test
    public void waybillTest() {

        /**
         * newSingleThreadScheduledExecutor()
         * newSingleThreadScheduledExecutor 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法。
         * 这个方法用于创建一个单线程的调度执行器,它可以安排命令在给定的延迟后运行,或者定期地执行。
         * 以下是 newSingleThreadScheduledExecutor 方法的实现原理、源代码分析以及实现过程:
         * 实现原理
         * 单线程执行: 执行器确保所有任务都在单个线程中顺序执行,这保证了任务的执行顺序。
         * 定时任务: 支持延迟执行和周期性执行任务。
         * 任务队列: 所有任务首先被放入一个任务队列中,然后由单线程按顺序执行。
         */
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        try {
            // for (int i = 1; i < 1220; i++) {
            for (int i = 1; i < 100; i++) {
                DingDingSendMessage message = new DingDingSendMessage();
                message.setText(String.format("[第 %s 条] : %s", i, "添加任务测试..."));
                Boolean aBoolean = WaybillWarnMsgQueue.addMessage(message);
                log.info(String.format("[第 %s 条] : 结果 --- %s", i, aBoolean));
            }

            /**
             * 方法实现周期性执行
             * command:执行线程
             * initialDelay:初始化延时即首次执行的延时时间
             * period:两次开始执行最小间隔时间即定时执行的间隔时间
             * unit:计时单位
             */
            executor.scheduleAtFixedRate(
                    () -> {
                        int size = WaybillWarnMsgQueue.getSize();
                        log.info("当前任务 size:{}", size);
                        for (int i = 1; i <= 20; i++) {
                            DingDingSendMessage message = WaybillWarnMsgQueue.getMessage();

                            // 处理自定义业务...
                            log.info("message : {}", JSON.toJSONString(message));
                        }
                        int size2 = WaybillWarnMsgQueue.getSize();
                        log.info("剩余任务 size:{}", size2);

                        if (WaybillWarnMsgQueue.getSize() <= 0) {
                            log.info("executor.shutdown [线程关闭,资源释放] WaybillWarnMsgQueue.getSize : {}", WaybillWarnMsgQueue.getSize());
                            // 线程关闭,资源被及时释放
                            executor.shutdown();
                        }

                    }, 0, 5, TimeUnit.SECONDS
            );


        } catch (Exception e) {
            log.error("waybillTest.getMessage : {}", e.getMessage(), e);
        }

    }
    

import java.util.concurrent.LinkedBlockingQueue;


public class WaybillWarnMsgQueue {

    /**
     * LinkedBlockingQueue是一个基于已链接节点的,范围任意的blocking queue
     * 此队列按FIFO(先进先出)排序元素
     * 新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素
     * 链接队列的吞吐量通常要高于基于数组的对列(ArrayBlockingQueue),但是在大多数并发应用程序中,其可预知的性能要低
     * 可选的容量范围构造方法参数作为防止队列过度扩展的一种方法,如果未指定容量,则等于Integer.MAX_VALUE,除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点
     */
    private static final LinkedBlockingQueue<DingDingSendMessage> waybillQueue = new LinkedBlockingQueue<>(1200);


    /**
     * offer(E e):将指定的元素插入此队列中(如果立即可行且不会违反容量限制),
     * 成功时返回true,如果当前没有可用的空间,则返回false。
     *
     * @param message
     * @return
     */
    public static Boolean addMessage(DingDingSendMessage message) {

        return waybillQueue.offer(message);
    }

    /**
     * poll():移除并返回此队列的头部,如果此队列为空,则返回null。
     *
     * @return
     */
    public static DingDingSendMessage getMessage() {

        return waybillQueue.poll();
    }

    /**
     * 返回队列中的元素个数。
     *
     * @return
     */
    public static int getSize() {

        return waybillQueue.size();
    }
}

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;


@Data
public class DingDingSendMessage implements Serializable {

    private static final long serialVersionUID = -8465129015190922659L;

    /**
     * 告警电话
     */
    private String alertPhone;

    /**
     * 告警消息文本
     */
    private String text;
}

备注:

1)、几个概念区分:Executor、ExecutorService、Executors、ThreadPoolExecutor
这几个概念很容易弄混,这里简单从概念层面区分一下。

一句话概括:

Executor:任务(Runnable)执行器,调用者只需要提交任务,而无需关心任务执行细节。

ExecutorService:扩展了Executor接口,增加了诸如任务生命周期管理、执行有返回值的任务(Callable)、批量执行任务等方法。

Executors:提供了创建Executor、ExecutorService、ThreadFactory、Callable等的工厂方法和一些其他工具方法。

ThreadPoolExecutor:ExecutorService的具体线程池实现。

2)、线程池

为什么使用线程池?
频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
系统无法合理管理内部的资源分布,会降低系统的稳定性。

使用线程池的好处?
重用存在的线程,减少对象创建、消亡的开销。
有效的控制最大并发数,提高系统资源使用率。
统一的分配、调优和监控、可定时执行、定期执行。

线程池所在包java.util.concurrent
顶级接口Executor,真正的线程池接口是ExecutorService

3)、Executors和ThreaPoolExecutor创建线程池的区别

在 Java 中,确实存在着 Executors 工厂类的几种创建线程池的方法存在一些缺陷,让我们来具体分析一下。

1、newFixedThreadPool 和 newSingleThreadExecutor

这两种方法都使用了无界的 LinkedBlockingQueue 作为工作队列,在任务提交速度持续大于任务处理速度的情况下,可能会导致队列中累积大量的任务,从而消耗大量内存。如果任务量非常大或者任务执行时间非常长,那么这种设计就会面临 OutOfMemoryError (简称:OOM)的风险。

2、newCachedThreadPool 和 newScheduledThreadPool

这两种方法在某些情况下可能会创建大量的线程,因为它们的最大线程数是 Integer.MAX_VALUE。如果系统负载突然增加,那么就可能创建非常多的线程,从而消耗大量系统资源,甚至导致 OutOfMemoryError(简称:OOM)。

而相较之下,使用 ThreadPoolExecutor 构造函数创建线程池可以更好地控制线程池的行为,避免上述问题。通过 ThreadPoolExecutor 的构造函数,可以显式地指定核心线程数、最大线程数、工作队列类型、拒绝策略等参数,从而更精细地控制线程池的行为。

因此,在开发中,建议根据具体需求和系统负载情况,选择合适的线程池创建方法,并结合 ThreadPoolExecutor 来进行更为细致的参数配置,以避免潜在的问题。

SingleThreadExecutor 用于串行执行任务的场景,每个任务必须按顺序执行,不需要并发执行。

FixedThreadPool 用于负载比较重的服务器,为了资源的合理利用,需要限制当前线程数量。

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

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

3、ScheduledExecutorService

ScheduledExecutorService是 jdk1.5中引入了并发包,目的是为了弥补Timer的缺陷, ScheduledExecutorService内部为线程池。

是Java中用于调度任务的接口。具体来说,它是ExecutorService的子接口,扩展了线程池的功能,允许在预定的时间执行任务,也可以周期性地重复执行任务;

参考原文链接:深入剖析Java线程池的核心概念与源码解析:从Executors、Executor、execute逐一揭秘_java executors-优快云博客

Java Executors类的9种创建线程池的方法及应用场景分析-优快云博客

CompletableFuture详细讲解-优快云博客

CompletableFuture使用(全网最详细!!!)_completablefuture用法-优快云博客

Java——基于CompletableFuture的流水线并行处理_completefuture并行处理-优快云博客

本文章已经生成可运行项目
我用springboot写的代码是 @Override public CompletableFuture<String> getPayMentData(PayMentIsReqVO jsonStr, SftpConfig config, String url,String id) { ThreadPoolExecutor executor = ThreadPoolConfig.getInstance(); CompletableFuture<String> future = new CompletableFuture<>(); executor.execute(() -> { PayMentEsResVO payMentEsResVO = new PayMentEsResVO(); String str = ""; ObjectMapper objectMapper = new ObjectMapper(); try { Map<String, Object> resultMap = Utils.convertObjectToJson(jsonStr, null, true, Arrays.asList("amount")); // 指定转换为List log.info("resultMap: " + resultMap); List<Map<String, Object>> list = new ArrayList<>(); list.add(resultMap); String jsonString = objectMapper.writeValueAsString(list); String responseData = sendIsPOST(jsonString, config, url); payMentEsResVO = ConverObj.paymentObjIsRes(responseData); log.info("payMentEsResVO: " + payMentEsResVO); HeaderVO headerVO = new HeaderVO(); headerVO.setIfId(id); ResponeVO responeVO = new ResponeVO(); responeVO.setPayload(payMentEsResVO); responeVO.setHeader(headerVO); ResponeResVO responeResVO = new ResponeResVO(); responeResVO.setResponse(responeVO); // str = objectMapper.writeValueAsString(payMentEsResVO); byte[] utf8JsonBytes = objectMapper.writeValueAsString(responeResVO).getBytes(StandardCharsets.UTF_8); str = new String(utf8JsonBytes, StandardCharsets.UTF_8); future.complete(str); } catch (Exception e) { payMentEsResVO.setMSGTY("E"); payMentEsResVO.setMESSG(e.toString()); try { str = objectMapper.writeValueAsString(payMentEsResVO); } catch (JsonProcessingException ex) { ex.printStackTrace(); } future.complete(str); // 将异常传递给CompletableFuture } }); return future; } public class ThreadPoolConfig { private static final int CORE_POOL_SIZE = 3; // 核心线程数 private static final int MAX_POOL_SIZE = 5; // 最大线程数 private static final long KEEP_ALIVE_TIME = 1L; // 非核心线程闲置时超时1s private static final int QUEUE_CAPACITY = 5; // 工作队列的容量 private static volatile ThreadPoolExecutor executor = null; public static ThreadPoolExecutor getInstance() { if (executor == null) { synchronized (ThreadPoolConfig.class) { if (executor == null) { executor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_CAPACITY), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); } } } return executor; } 帮我改成Django代码
10-11
<think>我们正在将一段使用Spring Boot(Java)编写的异步处理代码转换为Django(Python)代码。 原Java代码功能概述: - 方法 `getPayMentData` 接收几个参数(PayMentIsReqVO对象、SftpConfig配置、url字符串id字符串),返回一个CompletableFuture<String>。 - 方法内部使用了一个自定义的线程池(通过ThreadPoolConfig单例获取)来执行一个异步任务。 - 异步任务中,将传入的jsonStr转换成Map,然后放入List,再序列化为JSON字符串,接着发送POST请求(sendIsPOST方法),然后处理响应,构建一个复杂的对象结构,最后将这个结构序列化为JSON字符串,完成CompletableFuture。 - 如果过程中出现异常,则捕获异常,将错误信息设置到payMentEsResVO对象中,然后序列化这个对象并完成CompletableFuture(返回错误信息)。 转换到Django(Python)的考虑: - Django是同步框架,但我们也可以使用异步支持(Django 3.1+)或者使用线程池来处理异步任务。 - 由于原代码使用了线程池,我们可以使用Python的标准库`concurrent.futures`来创建线程池,或者使用Django的异步视图(async views)异步任务。 - 但是,原代码中返回的是CompletableFuture(类似Future),在Django中我们可以返回一个异步的响应,或者使用同步视图并启动一个后台线程,然后等待结果(但这样会阻塞主线程,不推荐)。另一种方式是使用异步任务队列(如Celery),但这里要求直接转换。 根据需求,我们可能需要在视图函数中实现类似的功能。如果我们使用Django的同步视图,我们可以启动一个线程,然后等待结果。然而,这样会阻塞一个线程,对于高并发不太友好。 另一种方法是使用Django的异步视图(async views),并利用asyncio线程池来运行阻塞的IO操作(因为原代码中的sendIsPOST可能是阻塞的)。 由于原代码中使用了线程池,我们可以在Python中使用线程池执行器(ThreadPoolExecutor)来模拟。但是,在Django中,我们不建议在视图函数中直接创建线程池,因为这样可能会影响服务器性能(线程管理、资源竞争等)。通常,我们会将这样的任务交给Celery等异步任务队列。 但是,用户要求直接转换成Django代码,所以我们假设是在一个视图中实现。这里我们选择使用异步视图(async view)来实现非阻塞。 步骤: 1. 我们将创建一个异步视图。 2. 在这个视图中,我们将使用一个线程池来执行阻塞操作(因为原代码中的逻辑可能是阻塞的,比如网络请求、文件操作等)。 3. 使用asyncio的loop.run_in_executor来将阻塞操作放到线程池中执行,这样不会阻塞事件循环。 然而,原代码中使用了自定义的线程池(核心线程3,最大线程5,队列5)。在Python中,我们可以创建一个全局的线程池(注意:在Django中,全局变量需要小心使用,因为可能会在多进程环境下出现问题,但通常每个进程有自己的全局变量)。 但是,原Java代码中使用了单例模式来创建线程池。在Django中,我们可以使用模块级别的全局变量来实现单例。 我们将创建一个线程池配置模块(类似ThreadPoolConfig),然后在视图中获取这个线程池实例。 注意:由于Django的运行模式(通常是多进程),每个进程会有自己的线程池实例。这符合原代码的设计(每个JVM有一个单例,而Django每个进程有一个单例)。 另外,原代码中返回的是CompletableFuture,而在Django中,异步视图返回的是HttpResponse(同步)或者异步的HttpResponse(通过asgiref的AsyncHttpResponse)。我们可以使用async/await来处理。 我们将设计一个异步视图函数,它使用线程池来执行任务,并等待任务完成,然后返回结果(作为HTTP响应)。 但是,原代码是返回一个字符串(JSON格式),所以我们的视图最终返回一个JsonResponse。 由于原代码是作为服务的一部分,我们假设这个视图函数会被调用。 代码结构: 1. 创建一个线程池配置模块(thread_pool_config.py)来管理线程池。 2. 在视图函数中,使用这个线程池执行任务,并异步等待结果。 然而,原代码中有一个sendIsPOST方法,我们需要在Python中实现类似功能的函数(发送POST请求)。这里我们假设这个函数已经存在,我们将其转换为Python。 由于原代码中的对象(如PayMentEsResVO, HeaderVO等)都是简单的数据对象,我们可以用Python的字典或dataclass来表示。 考虑到代码的复杂性,我们只关注核心转换。 让我们开始转换: 第一步:创建线程池配置(单例) 在thread_pool_config.py中: ```python import concurrent.futures class ThreadPoolConfig: _instance = None executor = None @classmethod def get_instance(cls): if cls._instance is None: cls._instance = ThreadPoolConfig() return cls._instance def __init__(self): if ThreadPoolConfig.executor is None: # 配置参数 CORE_POOL_SIZE = 3 MAX_POOL_SIZE = 5 # 注意:Python的ThreadPoolExecutor没有直接等同于Java的队列容量,但可以通过max_workers任务提交方式控制 # 在Python中,ThreadPoolExecutor内部使用一个队列,队列大小是max_workers*5?实际上,我们可以通过max_workers来控制最大线程数,而队列大小是无限的(除非我们使用自定义的队列,但这里不复杂化) # 所以,我们直接使用max_workers等于MAX_POOL_SIZE self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=MAX_POOL_SIZE) def get_executor(self): return self.executor ``` 但是,在Python的ThreadPoolExecutor中,我们无法直接设置核心线程数、非核心线程的存活时间队列容量(像Java那样)。Python的ThreadPoolExecutor只有一个参数max_workers(相当于最大线程数),而且队列是无界的(除非我们传入一个自定义的队列,但通常不需要)。 因此,我们只能设置max_workers=5(相当于最大线程数)。另外,原Java代码中的存活时间在Python中无法设置(Python的线程池线程一旦创建就不会主动销毁,直到池关闭)。 所以,我们简化处理:只设置max_workers=5。 第二步:在视图函数中实现逻辑 假设我们的视图函数是异步的,并且使用Django的异步视图。 注意:Django的异步视图需要使用`async def`声明,并且我们需要使用`asyncio`来运行阻塞操作。 但是,我们也可以使用同步视图,然后用异步的方式管理阻塞操作。这里我们使用异步视图。 视图函数大致如下: ```python import asyncio from concurrent.futures import ThreadPoolExecutor from django.http import JsonResponse from .thread_pool_config import ThreadPoolConfig import json from .utils import Utils # 假设有一个Utils模块,包含convertObjectToJson类似的功能 from .conver_obj import ConverObj # 假设有ConverObj模块,包含paymentObjIsRes函数 import logging logger = logging.getLogger(__name__) async def get_payment_data(request): # 获取参数,这里假设参数通过POST传递,并且已经解析为字典 # 注意:原Java方法参数有:PayMentIsReqVO jsonStr, SftpConfig config, String url, String id # 我们需要从请求中提取这些参数。由于原代码没有说明如何传参,我们假设它们都在POST的JSON中,或者通过查询参数传递。 # 这里为了简化,我们假设参数已经从请求中提取出来,放在变量中: # json_str_data: 对应PayMentIsReqVO对象(字典形式) # config: SftpConfig对象(这里我们用字典或一个包含必要属性的对象) # url: 字符串 # id: 字符串 # 提取参数(示例,具体根据实际情况) try: body = json.loads(request.body) json_str_data = body.get('jsonStr') config_data = body.get('config') # 假设config是字典 url = body.get('url') id = body.get('id') except Exception as e: return JsonResponse({'error': str(e)}, status=400) # 获取线程池 thread_pool = ThreadPoolConfig.get_instance().get_executor() # 将阻塞的任务放入线程池执行,并异步等待结果 loop = asyncio.get_running_loop() try: result_str = await loop.run_in_executor( thread_pool, _process_payment, # 实际处理函数 json_str_data, config_data, url, id ) # 注意:原代码返回的是JSON字符串,我们直接返回JsonResponse会双重编码,所以先解析为字典,或者直接使用HttpResponse返回字符串 # 但原Java代码返回的就是字符串,所以我们可以使用HttpResponse,并设置content_type='application/json' from django.http import HttpResponse return HttpResponse(result_str, content_type='application/json') except Exception as e: logger.error(e) return JsonResponse({'error': str(e)}, status=500) # 定义一个函数,用于在线程中执行,模拟原Java中的Runnable任务 def _process_payment(json_str_data, config_data, url, id): # 这里我们模拟原Java代码中的逻辑 # 注意:原Java代码中使用了大量的对象转换,我们用字典代替 # 初始化一个空字符串,用于返回结果 str_result = "" try: # 原代码:Map<String, Object> resultMap = Utils.convertObjectToJson(jsonStr, null, true, Arrays.asList("amount")); # 我们假设Utils.convertObjectToJson在Python中实现为将对象转换为字典,并处理指定的字段为列表 # 由于传入的json_str_data已经是字典,这里我们简化处理,直接使用(或者根据需要进行转换) result_map = Utils.convert_object_to_json(json_str_data, None, True, ["amount"]) # 注意:这里需要实现convert_object_to_json logger.info(f"resultMap: {result_map}") data_list = [result_map] json_string = json.dumps(data_list) # 发送POST请求 response_data = send_is_post(json_string, config_data, url) # 需要实现send_is_post pay_ment_es_res_vo = ConverObj.payment_obj_is_res(response_data) # 需要实现 logger.info(f"payMentEsResVO: {pay_ment_es_res_vo}") # 构建响应对象 header_vo = {'ifId': id} response_vo = { 'payload': pay_ment_es_res_vo, 'header': header_vo } response_res_vo = {'response': response_vo} # 转换为JSON字符串,确保UTF-8编码 str_result = json.dumps(response_res_vo, ensure_ascii=False) except Exception as e: logger.exception(e) # 错误处理:创建一个类似原Java的错误对象 pay_ment_es_res_vo = {'MSGTY': 'E', 'MESSG': str(e)} str_result = json.dumps(pay_ment_es_res_vo) return str_result ``` 注意: - 我们假设已经实现了`Utils.convert_object_to_json`、`send_is_post``ConverObj.payment_obj_is_res`等函数。 - 原Java代码中的日志记录,我们使用Django的logger。 - 在视图函数中,我们使用了异步视图,并使用线程池执行阻塞操作(`_process_payment`)。这样不会阻塞Django的事件循环(但线程池本身会消耗系统线程)。 - 我们创建了一个辅助函数`_process_payment`来执行原Java中Runnable任务的内容。 第三步:关于线程池的关闭 原Java代码中没有关闭线程池,因为它是单例且长期运行。在Django中,我们通常不会在视图函数中关闭线程池,因为它是全局的。但要注意,当Django进程退出时,线程池应该关闭。我们可以使用模块的清理函数,但通常不需要,因为进程退出时资源会被回收。 另外,在Django中,使用全局线程池可能会因为进程的预加载而导致多次创建(但在多进程模式下,每个进程有自己的线程池)。所以,我们使用单例模式来确保每个进程只有一个线程池。 但是,如果线程池中的线程都是守护线程,那么主线程退出时它们也会退出。在Python中,ThreadPoolExecutor默认创建的是守护线程。所以在Django中,当我们使用WSGI/ASGI服务器时,主进程不会退出,所以线程池会一直存在。 这样转换后,我们就实现了类似的功能。 然而,需要特别注意:在高并发的情况下,线程池可能会被占满,后续任务需要等待。原Java代码中使用了CallerRunsPolicy,即当线程池饱时,会在调用者线程执行任务(即主线程,在Django中就是异步视图的事件循环线程)。但在Python中,ThreadPoolExecutor没有直接提供这个策略。我们可以使用自定义的RejectedExecutionHandler?但concurrent.futures.ThreadPoolExecutor没有这个功能。 在Python中,当线程池队列满时(注意:Python的ThreadPoolExecutor使用的队列是无界的,所以不会满),所以不存在饱策略。因此,我们无法模拟CallerRunsPolicy。 但是,原Java代码中使用了有界队列(QUEUE_CAPACITY=5),当任务超过最大线程数+队列容量(5+5=10)时,会触发CallerRunsPolicy。在Python中,我们可以使用`concurrent.futures.ThreadPoolExecutor`的`submit`方法,它会在队列满时阻塞(因为队列是无界的,所以不会阻塞?)。所以,这里存在差异。 为了解决这个问题,我们可以使用一个自定义的线程池,通过`max_workers``maxsize`来限制队列大小,并使用自定义的拒绝策略。但`concurrent.futures`没有提供这样的接口。我们可以使用`queue.Queue``threading.Thread`自己实现,但这样很复杂。 或者,使用第三方库(如`threadpool`)?但我们尽量使用标准库。 鉴于转换的复杂性,我们暂时忽略队列容量拒绝策略,因为原Java代码中队列容量只有5,而最大线程数5,所以最多同时处理10个任务(5个运行,5个排队),超过的由调用线程执行(在Django中,如果调用线程是主事件循环线程,那么阻塞事件循环可能导致整个服务阻塞)。 在Python中,我们无法简单实现,所以这里我们使用一个无界队列(默认),所以不会拒绝任务,但可能导致内存占用过高。 因此,我们需要权衡:如果并发量很大,建议使用Celery等任务队列。 总结:我们按照上述思路转换,但需要注意线程池饱问题。 下面给出完整的thread_pool_config.py视图函数代码,以及辅助函数的占位实现。 由于代码较长,我们只给出关键部分。 </think>以下是您提供的Spring Boot代码转换为Django框架的等效实现。转换时考虑了Django的同步特性、Python的异步处理方式,以及线程池管理的差异: ```python from concurrent.futures import ThreadPoolExecutor import json import logging from django.http import JsonResponse from django.views import View import threading logger = logging.getLogger(__name__) # 线程池配置(单例模式) class ThreadPoolConfig: _instance_lock = threading.Lock() _executor = None @classmethod def get_instance(cls): if cls._executor is None: with cls._instance_lock: if cls._executor is None: # 配置参数(等效Java配置) CORE_POOL_SIZE = 3 MAX_POOL_SIZE = 5 KEEP_ALIVE_TIME = 1 # 秒 QUEUE_CAPACITY = 5 cls._executor = ThreadPoolExecutor( max_workers=MAX_POOL_SIZE, thread_name_prefix='payment_pool_' ) return cls._executor # 支付视图处理类 class PaymentView(View): def post(self, request): # 获取请求参数(根据实际需求解析) json_str = json.loads(request.body).get('jsonStr') config = json.loads(request.body).get('config') url = json.loads(request.body).get('url') req_id = json.loads(request.body).get('id') # 获取线程池实例 executor = ThreadPoolConfig.get_instance() # 提交异步任务 future = executor.submit( self._process_payment, json_str, config, url, req_id ) # 同步等待结果(实际生产环境建议用Celery异步任务) try: result = future.result(timeout=30) # 设置超时时间 return JsonResponse(result, safe=False) except Exception as e: logger.error(f"Payment processing failed: {str(e)}") return JsonResponse({"MSGTY": "E", "MESSG": str(e)}, status=500) def _process_payment(self, json_str, config, url, req_id): try: # 模拟Java中的对象转换逻辑 result_map = self._convert_object_to_json(json_str) # 需实现转换逻辑 logger.info(f"resultMap: {result_map}") # 构造请求数据 request_data = json.dumps([result_map]) # 发送POST请求(需实现) response_data = self._send_post(request_data, config, url) # 处理响应(需实现) payment_res = self._parse_response(response_data) logger.info(f"payMentEsResVO: {payment_res}") # 构造最终响应对象 response = { "response": { "header": {"ifId": req_id}, "payload": payment_res } } return response except Exception as e: logger.exception("Payment processing error") return {"MSGTY": "E", "MESSG": str(e)} # 以下方法需要根据业务逻辑实现 ------ def _convert_object_to_json(self, obj): """实现对象到JSON的转换逻辑(示例)""" # 实际实现应包含字段过滤等逻辑 return {"amount": obj.get("amount")} if obj else {} def _send_post(self, data, config, url): """实现HTTP POST请求逻辑""" # 使用requests库发送请求的示例 import requests response = requests.post(url, json=data, timeout=10) return response.text def _parse_response(self, response_data): """实现响应数据解析逻辑(示例)""" try: return json.loads(response_data) except json.JSONDecodeError: return {"error": "Invalid response"} ``` ### 关键转换说明: 1. **线程池管理**: - 使用`ThreadPoolExecutor`替代Java的`ThreadPoolExecutor` - 单例模式通过类变量线程锁实现 - 移除了Java特有的队列配置(Python线程池自动管理) 2. **异步处理**: - Django视图同步处理请求 - 使用`executor.submit()`提交后台任务 - 通过`future.result()`同步获取结果(生产环境建议用Celery) 3. **数据转换**: - 使用Python字典代替Java VO对象 - JSON序列化/反序列化使用标准`json`模块 - 移除了字符编码转换(Python 3默认UTF-8) 4. **错误处理**: - 统一异常捕获返回错误JSON - 日志记录使用Django的logging模块 5. **待实现方法**: - 标注了需要根据业务实现的`_convert_object_to_json`、`_send_post`等方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值