一, 创建线程
1. 创建普通对象, 只是在 JVM 的堆里分配一块内存而已
2. 创建线程, 需要调用操作系统内核的 API, 然后操作系统需要为线程分配一系列资源, 成本很高
线程是一个重量级对象, 应该避免频繁创建和销毁, 采用线程池方案
二, 一般的池化资源
- // 假设 Java 线程池采用一般意义上池化资源的设计方法
- class ThreadPool {
- // 获取空闲线程
- Thread acquire() {
- }
- // 释放线程
- void release(Thread t) {
- }
- }
- // 期望的使用
- ThreadPool pool;
- Thread T1 = pool.acquire();
- // 传入 Runnable 对象
- T1.execute(() -> {
- // 具体业务逻辑
- });
三, 生产者 - 消费者模式
业界线程池的设计, 普遍采用生产者 - 消费者模式, 线程池的使用方是生产者, 线程池本身是消费者
- public class MyThreadPool {
- // 工作线程负责消费任务并执行任务
- class WorkerThread extends Thread {
- @Override
- public void run() {
- // 循环取任务并执行
- while (true) {
- Runnable task = null;
- try {
- task = workQueue.take();
- } catch (InterruptedException e) {
- }
- task.run();
- }
- }
- }
- // 利用阻塞队列实现生产者 - 消费者模式
- private BlockingQueue<Runnable> workQueue;
- // 内部保存工作线程
- List<WorkerThread> threads = new ArrayList<>();
- public MyThreadPool(int poolSize, BlockingQueue<Runnable> workQueue) {
- this.workQueue = workQueue;
- for (int i = 0; i <poolSize; i++) {
- WorkerThread work = new WorkerThread();
- work.start();
- threads.add(work);
- }
- }
- // 提交任务
- public void execute(Runnable command) throws InterruptedException {
- workQueue.put(command);
- }
- public static void main(String[] args) throws InterruptedException {
- // 创建有界阻塞队列
- BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);
- // 创建线程池
- MyThreadPool pool = new MyThreadPool(10, workQueue);
- // 提交任务
- pool.execute(() -> {
- System.out.println("hello");
- });
- }
- }
四, Java 线程池
Ⅰ. ThreadPoolExecutor
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,
- RejectedExecutionHandler handler)
- // 让所有线程都支持超时, 如果线程池很闲, 那么将撤销所有线程
- public void allowCoreThreadTimeOut(boolean value)
1.corePoolSize: 线程池保有的最小线程数
2.maximumPoolSize: 线程池创建的最大线程数
3.keepAliveTime & unit
如果一个线程空闲了 keepAliveTime & unit, 并且线程池的线程数大于 corePoolSize, 那么这个空闲的线程就要被回收
4.workQueue: 工作队列
5.threadFactory: 自定义如何创建线程
6.handler
线程池中的所有线程都很忙碌, 并且工作队列也满了 (工作队列是有界队列), 此时提交任务, 线程池会拒绝接收
CallerRunsPolicy: 提交任务的线程自己去执行该任务
AbortPolicy: 默认的拒绝策略, 抛出 RejectedExecutionException
DiscardPolicy: 直接丢弃任务, 不会抛出任何异常
DiscardOldestPolicy: 丢弃最老的任务, 然后把新任务加入到工作队列中
Ⅱ. Executors
1. 不建议使用 Executors, 因为 Executors 提供的很多默认方法使用的是 *** 队列 LinkedBlockingQueue
2. 在高负载的情况下,*** 队列容易导致 OOM, 而 OOM 会导致所有请求都无法处理
3. 因此强烈建议使用有界队列
Ⅲ. 拒绝策略
1. 使用有界队列, 当任务过多时, 线程池会触发拒绝策略
2. 线程池默认的拒绝策略会抛出 RejectedExecutionException, 这是一个运行时异常, 开发时很容易忽略
3. 如果线程池处理的任务非常重要, 可以自定义拒绝策略
Ⅳ. 异常处理
1. 使用 ThreadPoolExecutor.execute() 方法提交任务时, 如果任务在执行过程中出现运行时异常
会导致执行任务的线程终止, 并且无法获得任何通知
2. 因此最稳妥的方法还是捕获所有异常并处理
- try {
- // 业务逻辑
- } catch (RuntimeException x) {
- // 按需处理
- } catch (Throwable x) {
- // 按需处理
- }
写在最后
来源: http://www.bubuko.com/infodetail-3103200.html