创建线程的几种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口(有返回值)
- 线程池创建
线程池的实现方式
- Executors.newFixedThreadPool(5); 一池固定线程:固定大小线程池,适用于任务数量固定,执行时间较长的任务
- Executors.newSingleThreadExecutor(); 一池一线程:单线程池,可以保证任务的顺序性
- Executors.newCachedThreadPool(); 一池多线程:可以无限扩大,适合任务量大,但是执行时间很短的线程,这种线程不会造成CPU的过度切换
- newScheduledThreadPool:适用于执行延迟或者周期性的任务
那么实际使用就是这样的
ExecutorService es=new ThreadPoolExecutor(7个参数);
参数解释:
- corePoolSize: 线程池常驻核心线程数
- maxImumPoolSize: 线程池能够容纳同时执行的最大线程数,必须大于等于1
- keepAliveTime: 多余的空闲线程的存活时间
- unit: keepAliveTime的单位
- workQueue: 任务队列,被提交但尚未被执行的任务
- threadFactory: 线程池线程工厂,一般用默认的
- handler: 拒绝策略,当队列满了并且工作线程大于等于线程池的最大线程数 maxImumPoolSize 会执行拒绝策略
4种拒绝策略:
- AbortPolicy(默认):直接抛出RejectedExecutionException 异常组织系统正常运行
- CallerRunsPolicy:该策略不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
- DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
- DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这种策略是可行的
1、4都好理解。第2种解释一下:我去A银行办理业务,A银行人员说这里满了,让我去B银行处理;到了B银行,B银行说这里也满了,谁叫你来的你叫谁处理,所以我又回到了A银行等待办理。
核心线程数如何决定:
最大线程数=max+阻塞队列数
最大线程数参数应该怎么设置?
需要看具体业务是做什么,分为CPU计算密集型和IO密集型。
先查看机子是几核的,再根据具体类型进行计算
Runtime.getRuntime().availableProcessors();
CPU密集型
- 一般公式:CPU核心数+1个线程的线程池
IO密集型
线程的创建必须通过线程池:
线程池的工作原理
通俗点来说:
核心线程数:内部员工;阻塞队列:任务仓库;非核心线程:外包人员,仓库堆满了,任务办不完,让外包人员做, 如果外包人员达到最大数量,还是做不完,就交给拒绝策略
工作原理流程图
右半部分流程用文字描述:
1.在创建了线程池后,等待提交过来的任务请求
2.当调用execute() 方法添加一个请求任务时线程池会进行判断:
2.1 如果正在运行的线程数量小于corePollSize,那么马上创建线程运行这个任务;
2.2 如果正在运行的线程数量大于或等于corePoolSize,那么任务会放入队列;
2.3 如果队列满了并且运行的线程数量小于 最大线程数(maximunPoolSize),那么会创建非核心线程运行任务
2.4 如果队列满了并且运行的线程数量大于等于最大线程数,那么就执行拒绝策略
3. 当线程空出来无事可做的时候超过一定的时间(keepAliveTime),线程池会判断如果当前运行的线程数量大于核心线程数(corePoolSize),那么空出来的线程会被停掉