线程池概念及相关使用 ThreadPoolExecutor ExecutorCompletionService

线程池概念

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

使用线程池目的

避免频繁的创建销毁线程,复用创建的线程。

线程池处理流程

在这里插入图片描述
通过上图,我们看到了线程池的主要处理流程。我们的关注点在于,任务提交之后是怎么执行的。大致如下:

  1. 判断核心线程池是否已满,如果不是,则创建线程执行任务
  2. 如果核心线程池满了,判断队列是否满了,如果队列没满,将任务放在队列中
  3. 如果队列满了,则判断线程池是否已满,如果没满,创建线程执行任务
  4. 如果线程池也满了,则按照拒绝策略对任务进行处理

线程池实际使用

线程池的创建

  1. 使用Executors创建:Executors是线程池工厂类,不需要我们自己去实现,实际创建的是ThreadPoolExecutor实例。
  2. 自己手动创建ThreadPoolExecutor实例
    注意:阿里Java开发手册要求【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

线程池相关接口/类继承关系如下

在这里插入图片描述

对比Executor和new Thread()

new Thread的弊端如下:
  • 每次new Thread新建对象性能差。
  • 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
  • 缺乏更多功能,如定时执行、定期执行、线程中断。
Java提供的四种线程池的好处在于:
  • 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • 提供定时执行、定期执行、单线程、并发数控制等功能。

Executor 和 ExecutorService 接口主要的区别:

  • ExecutorService 接口继承了 Executor 接口,是 Executor 的子接口
  • Executor 接口定义了 execute()方法用来接收一个Runnable接口的对象,而 ExecutorService 接口中的 submit()方法重载可以接受两种Runnable和Callable接口的对象。
  • Executor 中的 execute() 方法不返回任何结果,而 ExecutorService 中的 submit()方法可以通过一个 Future 对象返回运算结果。
  • ExecutorService 还提供用来控制线程池的方法。比如:调用 shutDown() 方法终止线程池。

Executors使用

省略,参考见下文

ThreadPoolExecutor使用

1、构建ThreadPoolExecutor对象,构造方法参数:

序号名称类型含义
1corePoolSizeint核心线程池大小
2maximumPoolSizeint最大线程池大小
3keepAliveTimelong线程最大空闲时间
4unitTimeUnit时间单位
5workQueueBlockingQueue线程等待队列
6threadFactoryThreadFactory线程创建工厂
7handlerRejectedExecutionHandler拒绝策略

2、ThreadPoolExecutor的处理流程
在这里插入图片描述
3、自定义ThreadPoolExecutor线程池

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

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException, IOException {
        int corePoolSize = 2;
        int maximumPoolSize = 4;
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        ThreadFactory threadFactory = new NameTreadFactory();
        RejectedExecutionHandler handler = new MyIgnorePolicy();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
                workQueue, threadFactory, handler);
        executor.prestartAllCoreThreads(); // 预启动所有核心线程
        for (int i = 1; i <= 10; i++) {
            MyTask task = new MyTask(String.valueOf(i));
            executor.execute(task);
        }

        Thread.sleep(Integer.MAX_VALUE); //阻塞主线程
    }

    static class NameTreadFactory implements ThreadFactory {
        private final AtomicInteger mThreadNum = new AtomicInteger(1);
        
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
            System.out.println(t.getName() + " has been created");
            return t;
        }
    }

    public static class MyIgnorePolicy implements RejectedExecutionHandler {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            doLog(r, e);
        }

        private void doLog(Runnable r, ThreadPoolExecutor e) {
            // 可做日志记录等
            System.err.println( r.toString() + " rejected");
        }
    }

    static class MyTask implements Runnable {
        private String name;

        public MyTask(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                System.out.println(this.toString() + " is running!");
                Thread.sleep(3000); //让任务执行慢点
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "MyTask [name=" + name + "]";
        }
    }
}

Console log

my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
MyTask [name=2] is running!
MyTask [name=1] is running!
my-thread-4 has been created
MyTask [name=3] is running!
MyTask [name=6] is running!
MyTask [name=7] rejected
MyTask [name=8] rejected
MyTask [name=9] rejected
MyTask [name=10] rejected
MyTask [name=4] is running!
MyTask [name=5] is running!

ExecutorCompletionService使用

ExecutorCompletionService提供了对线程池的封装
注意:处理流程判断顺序:核心线程->队列->非核心线程->拒绝策略

对比completionService take poll方法

  • completionService.take()// 阻塞,直到有任务完成可以获取结果
  • completionService.poll()//poll直接返回,不阻塞。但是没有完成的任务则返回null
  • completionService.poll(5,TimeUnit.SECONDS) //阻塞等待指定时间,如果有完成结果返回,没有的直接返回null
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class ExecutorCompletionServiceUtil {
    /**
     * 线程数(处理器数量)
     */
    private static final int threadCount = Runtime.getRuntime().availableProcessors();

    public static void main(String[] args) {
        ExecutorCompletionServiceUtil executorCompletionServiceUtil = new ExecutorCompletionServiceUtil();
        executorCompletionServiceUtil.test();
    }

    public void test() {
        System.out.println("threadCount:" + threadCount);
        Executor executor = Executors.newFixedThreadPool(threadCount);
        ExecutorCompletionService service = new ExecutorCompletionService(executor);
        for (int i = 0; i < 100; i++) {
            service.submit(new TaskDemo(i));
        }
        List<String> list = new ArrayList<>(100);
        for (int i = 0; i < 100; i++) {
            try {
                String result = (String) service.take().get();
                System.out.println(result);
                list.add(result);
            } catch (InterruptedException e) {
                System.err.println("InterruptedException" + e);
            } catch (ExecutionException e) {
                System.err.println("ExecutionException" + e);
            }
        }
        System.out.println("result:" + list);
        System.out.println("result size:" + list.size());
    }

    /**
     * 执行任务需要实现Callable接口
     */
    class TaskDemo implements Callable<String> {
        private int number;

        public TaskDemo(int number) {
            this.number = number;
        }

        @Override
        public String call() throws Exception {
            return "Task:" + Thread.currentThread().getName() + " number:" + number;
        }
    }
}

Console log

threadCount:8
Task:pool-1-thread-1 number:0
Task:pool-1-thread-4 number:3
Task:pool-1-thread-2 number:1
.........................
Task:pool-1-thread-3 number:96
Task:pool-1-thread-7 number:97
result:[Task:pool-1-thread-1 number:0, Task:pool-1-thread-4 number:3, ...................Task:pool-1-thread-3 number:96, Task:pool-1-thread-7 number:97]
result size:100

参考:

  • 线程池实现原理参考 https://www.jianshu.com/p/7ab4ae9443b9
  • 线程池配置参数参考https://blog.csdn.net/q669239799/article/details/90680701
  • ThreadPoolExecutor参数、方法详解 https://www.jianshu.com/p/c41e942bcd64
  • ThreadPoolExecutor使用,自定义 https://www.jianshu.com/p/f030aa5d7a28
  • ExecutorCompletionService原理分析 https://www.jianshu.com/p/9b85c47a6ff4
  • CompletionService和ExecutorCompletionService详解 https://www.jianshu.com/p/cfda708a3478
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值