手动创建线程池以及线程拒绝策略

本文详细介绍了如何手动创建线程池,包括核心线程数、最大线程数、线程空闲时间等参数的意义。同时,深入探讨了四种标准的线程池拒绝策略:AbortPolicy、DiscardPolicy、DiscardOldestPolicy和CallerRunsPolicy,并提供了一种自定义拒绝策略的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于之前说使用Exectors类创建线程池时候,会造成OOM,建议手动创建线程池。

今天就尝试手动创建线程池,并且介绍它的4中拒绝策略和手动写拒绝策略。

在创建线程池之前,我们先看看线程池创建的构造函数和各个字段的意义。

 

corePoolSize:核心线程数,当线程数未达到核心线程时,新的任务会创建新的线程,即使有线程空闲也会创建新的线程。
maximumPoolSize:线程最大数,当阻塞队列满了之后,就会创建新的线程,达到最大线程数为止。
keepAliveTime:线程空闲时间,当线程空闲时间超过此值,并且线程数大于核心线程,此线程将被回收,以此来保证核心线程数量。
unit:线程空余时间单位。
workQueue:阻塞队列,当任务大于线程数,阻塞队列还未满,就会加入阻塞队列中
threadFactory:管理创建的线程,可以设置线程的名称等属性。
handler:拒绝策略,当阻塞对面已满,线程达到最大线程,就会采用阻塞策略。

知道了各个参数的意义,现在来手动创建线程池。

先创建一个任务,再创建一个线程池。

package test.thread.pool;

public class Task implements Runnable {

    private int i;

    public Task(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " print " + i);
    }

}
package test.thread.pool;

import java.util.concurrent.*;

public class CreateThreadPool {

    public static void main(String[] args) {
        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, blockingQueue,
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 10; i++) {
            Task task = new Task(i);
            executor.execute(task);
        }
        executor.shutdown();
    }

}

我们创建了一个核心线程数为2个,最大线程数为4,线程空闲时间为60s,阻塞队列大小为2的线程池,阻塞策略为默认的

AbortPolicy 根据源码可以看到,此种拒绝策略直接抛出rejectedExecution异常,执行结果如下。

pool-1-thread-1 print 0
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task test.thread.pool.Task@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Running, pool size = 4, active threads = 3, queued tasks = 0, completed tasks = 2]
pool-1-thread-2 print 1
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
pool-1-thread-4 print 5
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
pool-1-thread-1 print 2
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
pool-1-thread-3 print 4
	at test.thread.pool.CreateThreadPool.main(CreateThreadPool.java:15)
pool-1-thread-4 print 3
DiscardPolicy 拒绝策略,这种策略是不会做任何处理,不会抛出异常,直接丢弃任务,运行结果如下

import java.util.concurrent.*;

public class CreateThreadPool {

    public static void main(String[] args) {
        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, blockingQueue,
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 10; i++) {
            Task task = new Task(i);
            executor.execute(task);
        }
        executor.shutdown();
    }

}

 

运行结果如下,可以看到没有抛出异常,不做任何处理,直接丢弃了后面的任务。

pool-1-thread-2 print 1
pool-1-thread-3 print 4
pool-1-thread-2 print 2
pool-1-thread-4 print 5
pool-1-thread-1 print 0
pool-1-thread-3 print 3

Process finished with exit code 0
DiscardOldestPolicy 拒绝策略是从队列中移除老的任务,继续执行任务。

 

由运行结果可以看到,前面2,3,6,7任务被丢弃了。

pool-1-thread-1 print 0
pool-1-thread-2 print 1
pool-1-thread-2 print 9
pool-1-thread-1 print 8
pool-1-thread-3 print 4
pool-1-thread-4 print 5

Process finished with exit code 0


CallerRunsPolicy 是不会丢弃任务,直接运行任务的run方法,即使用主线程执行任务,执行结果如下。

 

根据运行结果可以看到,其中有main线程执行任务,因为当线程不足时,会由主线程直接执行run方法。

main print 6
pool-1-thread-4 print 5
pool-1-thread-2 print 1
pool-1-thread-2 print 3
pool-1-thread-1 print 0
pool-1-thread-3 print 4
pool-1-thread-4 print 2
main print 7
pool-1-thread-2 print 8
pool-1-thread-1 print 9

Process finished with exit code 0

 

以上就是线程池的4种拒绝策略,但是并不是很好,还是建议大家手动创建拒绝策略,下面给大家提供一种拒绝策略,供大家参考,当线程拒绝时,还是重复向队列中加,加进去继续让线程处理,执行结果如下。

package test.thread.pool;

import java.util.concurrent.*;

public class CreateThreadPool {

    public static void main(String[] args) {
        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, blockingQueue,
                Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if (!executor.isShutdown()) {
                    try {
                        executor.getQueue().put(r);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        for (int i = 0; i < 10; i++) {
            Task task = new Task(i);
            executor.execute(task);
        }
        executor.shutdown();
    }

}

 

运行结果可以看到,任务都被执行,并且没有使用主线程运行。

pool-1-thread-1 print 0
pool-1-thread-4 print 5
pool-1-thread-3 print 4
pool-1-thread-2 print 1
pool-1-thread-1 print 2
pool-1-thread-4 print 3
pool-1-thread-1 print 8
pool-1-thread-3 print 6
pool-1-thread-2 print 7
pool-1-thread-4 print 9

Process finished with exit code 0

 

最后再贴上更改线程名称等属性的代码

package test.thread.pool;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CreateThreadPool {

    static class NameThreadFactory implements ThreadFactory {

        private AtomicInteger count = new AtomicInteger();
        private String name;

        public NameThreadFactory(String name) {
            this.name = name + "-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, name + count.incrementAndGet());
            if (t.isDaemon()) {
                t.setDaemon(false);
            }
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }
    }

    public static void main(String[] args) {
        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, blockingQueue,
                new CreateThreadPool.NameThreadFactory("test-thread-pool"), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if (!executor.isShutdown()) {
                    try {
                        executor.getQueue().put(r);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        for (int i = 0; i < 10; i++) {
            Task task = new Task(i);
            executor.execute(task);
        }
        executor.shutdown();
    }

}

 

运行结果如下,可以看到线程名称变化

test-thread-pool-1 print 0
test-thread-pool-2 print 1
test-thread-pool-2 print 3
test-thread-pool-1 print 2
test-thread-pool-2 print 6
test-thread-pool-1 print 7
test-thread-pool-2 print 8
test-thread-pool-1 print 9
test-thread-pool-3 print 4
test-thread-pool-4 print 5

Process finished with exit code 0

以上就是手动创建线程,以及创建线程策略和线程属性的例子,如有不对的地方,还请指出,谢谢

### 手动创建线程池的方法 手动创建线程池可以通过 `ThreadPoolExecutor` 类来实现。这是一个灵活的类,允许开发者根据需求配置线程池的核心参数[^5]。 以下是创建线程池的关键步骤: #### 配置核心参数 1. **核心线程(corePoolSize)** 这是指定线程池中始终保持活动状态的最小线程数量。即使这些线程处于空闲状态,也不会被销毁,除非设置了允许核心线程超时的选项。 2. **最大线程(maximumPoolSize)** 当任务队列已满且当前运行的线程数小于此值时,线程池会继续创建线程直到达到这个上限。 3. **保持活跃时间 (keepAliveTime)** 超过核心线程数之外的线程,在空闲状态下存活的时间长度。如果在此时间内未接收到新任务,则会被终止并移除。 4. **时间单位 (unit)** 定义上述 `keepAliveTime` 的时间单位,例如秒 (`TimeUnit.SECONDS`) 或毫秒 (`TimeUnit.MILLISECONDS`)[^5]。 5. **任务队列 (workQueue)** 存储待处理任务的阻塞队列。常见的实现包括 `ArrayBlockingQueue`, `LinkedBlockingQueue`, 和 `SynchronousQueue` 等。 6. **拒绝策略 (handler)** 当线程池无法再接受新任务(即达到了最大线程数且任务队列已满)时,采取的应对措施。Java 提供了四种内置的拒绝策略:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy 和 DiscardPolicy。 #### 实现代码示例 以下是一个完整的自定义线程池创建示例: ```java import java.util.concurrent.*; public class CustomThreadPool { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, // 核心线程数 10, // 最大线程数 1L, // 空闲线程存活时间 TimeUnit.SECONDS, // 时间单位 new ArrayBlockingQueue<>(100), // 任务队列,容量为100 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); // 提交任务到线程池 for (int i = 0; i < 200; i++) { final int taskNumber = i; threadPoolExecutor.submit(() -> { System.out.println("Executing Task " + taskNumber); try { Thread.sleep(100); // 模拟任务耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } // 关闭线程池 threadPoolExecutor.shutdown(); } } ``` 这段代码展示了如何通过 `ThreadPoolExecutor` 构造函数创建一个具有特定参数的线程池,并提交一批任务给它执行。 --- ### §相关问题§ 1. 如何选择适合的任务队列类型? 2. 不同的拒绝策略分别适用于哪些场景? 3. 使用固定大小线程池(FixedThreadPool)有哪些潜在的风险? 4. 在高并发环境下,如何调整线程池参数以优化性能? 5. 如果线程池中的线程发生异常退出,该如何捕获和恢复?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值