线程初始化的几种方式
- 继承Thread基类
- 实现Runnable接口
- 定义Runnable接口的实现类,重写该接口的run()方法
- 匿名内部类
- lambda表达式:函数式接口,有且仅有一个抽象方法,可以有default、static方法,可以使用@FunctionalInterface注解强制约束
- 实现Callable接口 + FutureTask
- 重写Callable接口中的call方法
- 可以抛出异常
- 可以有返回结果
- 使用的过程:
- 实现一个Callable的实现类
- 初始化Callable实现类对象
- 初始化一个FutrueTask对象,把Callable实现类对象作为构造参数
- 以FutureTask对象作为参数初始化一个Thread对象
- Future接口的特点
- 定义一个未来任务,在将来的某个时刻可以获取子任务的执行结果集
- 可以捕获子任务的异常信息
- 可以获取子任务的返回结果集 - 阻塞子线程
- 可以轮询获取子任务的执行状态
- 复用既有的结果集
- 重写Callable接口中的call方法
- 线程池
线程池
前三种创建线程的方式,都无法控制线程数。
特点:
- 控制线程数
- 复用既有线程
- 管理线程资源
优势:
- 性能高
- 方便管理
- 避免资源浪费
结构
结构:顶层接口Executor(execute) --> 子接口ExecutorService(submit shutdown) --> 抽象类AbstractExecutorService --> 实现类ThreadPoolExecutor
实现类 Executors
- newFixedThreadPool(线程数):固定大小的线程池,阻塞队列是最大整型
- newCachedThreadPool():无限大小的线程池,最大可扩展线程数是最大整型
- newSingleThreadExecutor():单个大小的线程池,阻塞队列是最大整型
- newScheduledThreadPool(线程数):定时任务的线程池,最大可扩展线程数是最大整型
不推荐使用Executors工具类初始化线程池,因为他们都可能导致OOM。
自定义线程池
ThreadPoolExecutor七个重要的参数
- corePoolSize:核心线程数
- maximumPoolSize:最大可扩展线程数
- keepAliveTime:扩展线程数的空闲时间
- unit:时间单位
- BlockingQueue:阻塞队列
- threadFactory:线程工厂
- RejectedExecutionHandler:拒绝策略
- AbortPolicy:抛出异常
- CallerRunsPolicy:调用者执行
- DiscardOldestPolicy:丢弃等待最久的
- DiscardPolicy:默默的丢弃一个
- 自定义拒绝策略:实现RejectedExecutionHandler
工作原理
- 线程池初始化之后,线程数为0
- 向线程池提交一个任务,此时线程池底层会做出如下判断
- 判断核心线程数是否已满,未满创建新的线程处理该任务
- 如果核心线程数已满,判断阻塞队列是否已满,未满则放入阻塞队列
- 如果阻塞队列已满,判断最大可扩展性线程数是否已满,未满则创建新的线程处理该任务
- 如果最大可扩展线程数已满,执行拒绝策略
- 一个任务执行完之后,线程会从阻塞队列中获取下一个任务执行
- 一个线程如果空闲时间到达生存时间,依然要做出判断:判断当前线程数是否大于核心线程数,则销毁当前线程。直至核心线程数