在手写线程池时,通常选择使用 有界队列无界队列,具体选择哪一种取决于具体的应用场景和需求。下面是每种队列的优缺点,并解释为什么在手写线程池时通常选择其中的某些队列。

1. 有界队列(ArrayBlockingQueue

  • 使用场景:对于大多数场景,尤其是当任务数量较为稳定或可控时,有界队列 是一个较好的选择。它通常用于大多数生产环境中的线程池设计中。
  • 为什么使用:有界队列的最大优势是可以限制线程池中任务的数量,防止任务积压造成内存耗尽或系统崩溃。

    • 例如:假设我们设定了最大线程数(比如 10 个线程),同时设定队列容量(比如 100 个任务)。当队列满时,线程池会拒绝新的任务或者阻塞提交任务的线程,直到队列有空闲位置。这种机制有效避免了内存爆炸和资源超载。
    • 优点:有界队列能有效防止任务数量过多导致内存泄漏等问题,保证系统的稳定性,能够帮助我们实现合理的负载控制。
    • 缺点:如果任务提交速度过快,队列满了,后续任务可能会被拒绝或需要长时间阻塞。

    常见队列选择ArrayBlockingQueue

    代码示例:

    ExecutorService executorService = new ThreadPoolExecutor(
        4, // corePoolSize
        10, // maximumPoolSize
        60L, TimeUnit.SECONDS, // keepAliveTime
        new ArrayBlockingQueue<>(100), // 队列
        new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
    );

2. 无界队列(LinkedBlockingQueue

  • 使用场景:如果任务提交和处理的速度相对均衡,且我们不关心队列是否会过多积压任务,无界队列 也是一个常见的选择。
  • 为什么使用:无界队列能够承接无限量的任务,避免了因任务队列大小限制而导致的拒绝或阻塞问题。在流量较大时可以避免线程池因队列满而拒绝任务。

    • 优点:任务可以无限制地加入队列,线程池的核心线程池数和最大线程数不需要频繁调整,适应性较强。
    • 缺点:如果任务提交速率过高,队列中的任务会持续增加,可能导致内存消耗过多,甚至出现 OutOfMemoryError。另外,线程池中的线程数也会不断增加,导致线程数过多时会有额外的上下文切换开销。

    常见队列选择LinkedBlockingQueue

    代码示例:

    ExecutorService executorService = new ThreadPoolExecutor(
        4, // corePoolSize
        10, // maximumPoolSize
        60L, TimeUnit.SECONDS, // keepAliveTime
        new LinkedBlockingQueue<>() // 无界队列
    );

3. 同步队列(SynchronousQueue

  • 使用场景:这种队列适用于需要快速处理每个任务的场景,通常任务处理时间较短且每个任务都需要独立的线程执行。
  • 为什么使用SynchronousQueue 是一个不缓存任何任务的队列,它会直接将任务交给线程池中的线程来执行,如果没有线程空闲,提交任务的线程将被阻塞,直到有线程来处理任务。

    • 优点:没有任务积压,适合需要高并发且任务时间短的场景,每个任务都会被迅速处理。
    • 缺点:没有缓存任务,可能导致频繁创建和销毁线程,影响性能。

    常见队列选择SynchronousQueue

    代码示例:

    ExecutorService executorService = new ThreadPoolExecutor(
        4, // corePoolSize
        10, // maximumPoolSize
        60L, TimeUnit.SECONDS, // keepAliveTime
        new SynchronousQueue<>() // 同步队列
    );

总结:

  • 有界队列(ArrayBlockingQueue:常用,因为它可以有效控制任务的数量,避免系统因为任务过多而崩溃。适用于任务量大且可控的场景。
  • 无界队列(LinkedBlockingQueue:适合任务数目不确定且任务提交速率较低的情况,虽然它不会拒绝任务,但需要小心内存消耗。
  • 同步队列(SynchronousQueue:适合极短时间、独立线程执行的任务场景,常用于高并发、需要快速响应的场景。

因此在手写线程池时,通常会选择 ArrayBlockingQueueLinkedBlockingQueue,具体根据任务的性质和系统的负载来决定:

  • 如果任务提交量较大且能控制系统负载,通常选择 ArrayBlockingQueue,它能避免系统崩溃。
  • 如果任务不太频繁且可以容忍一些任务积压,使用 LinkedBlockingQueue 是一个不错的选择。

今夜有点儿凉
40 声望1 粉丝

今夜有点儿凉,乌云遮住了月亮。