文章目录
1. 线程
1.1 线程是什么
一条代码的执行流,完成一组代码,通常将这一组代码称为一个任务
1.2 线程的本质
将代码送给cpu执行
1.3 用多线程的目的
充分利用cpu资源,并发做多件事
1.4 单核cpu机器上适不适用多线程
适用,如果是单线程,线程中等待IO时,此时cpu就空闲了
1.5 线程什么时候让出cpu
- 阻塞,wait,awit,等待I/O
- sleep
- yield
- 线程结束
1.6 cpu在多线程中的作用
执行代码
1.7 线程是不是越多越好
不是,因为线程的创建和销毁需要时间;线程是占用内存的;线程切换影响性能:
- 线程在java中是一个对象,每一个java线程需要一个操作系统线程支持。线程的创建,销毁都需要时间。如果创建时间+销毁时间 > 任务执行时间 就不合算了
- java对象占用堆内存,线程占用内存,根据jvm规范,一个线程默认最大栈大小1M,这个栈空间需要从内存中分配,所以如果线程过多,会消耗过多的内存
- 频繁切换线程上下文(时间片),影响性能
1.8 如何确定线程的合适数量
- 如果是计算型任务
cpu数量的1—2倍 - 如果是IO型任务
则多一些线程,要根据具体的I/O阻塞时长进行考量决定,如tomcat中默认最大线程为200,也可以考虑根据需要在一个最小和最大数量间自动增减线程数
2. 线程池
2.1 组成
![](https://i-blog.csdnimg.cn/blog_migrate/320c6cf334555325b9dc4e5f8789a805.png)
- 仓库: 存放任务,任务Runnable,Callable表示,用BlockingQueue阻塞队列存放任务,它是线程安全的。在队列为空时的获取阻塞,在队列满时放入阻塞,BlockingQueue方法有4种形式:
- 抛出异常
- 返回特殊值(null或false)
- 在操作成功前,无限期阻塞当前线程
- 在放弃前只在给定的最大时间限制内阻
抛出异常 | 特殊值 | 阻塞 | 超时 | |
插入 | add(e) | off(e) | put(e) | offer(e,time,unit) |
移除 | remove() | pull(e) | take(e) | pull(e,time,unit) |
检查 | element() | peek(e) | 不可用 | 不可用 |
- 线程
2.2 状态
分为5种状态:running,shutdown,shot,tidying,terminated,之间转换参见:https://www.cnblogs.com/skywang12345/p/3509960.html
2.3 拒绝策略
拒绝指的是:当任务添加到线程池中时被拒绝,之所以被拒绝,可能由于:@1.线程池异常关闭,@2.任务数量超过线程池的最大限制。共有4种拒绝策略:
- AbortPolicy :当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
- CallerRunsPolicy: 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
- DiscardOldestPolicy: 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
- DiscardPolicy: 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。
关于更多拒绝策略详情参见:https://www.cnblogs.com/skywang12345/p/3512947.html
2.4 工作原理
- 接收任务,放入仓库
- 工作线程从仓库取任务,送给cpu运行
- 当没有任务时,线程阻塞;当有任务时唤醒线程执行
3. JAVA并发包中线程池API使用
![](https://i-blog.csdnimg.cn/blog_migrate/7de0b39ab2b192bd7f19c9ca2a4ef035.png)
提供了连个工具类来创建工厂:Executor和Executors(存在陷阱)
- Executor
- ExecutorService
- ForkJoinPool:很少用,适用于计算型线程
- ThreadPoolExecutor:基础、标准线程池实现(其中需注意的是,有核心线程和非核心线程的区别,比如创建了一个无界的仓库,100个线程,10个核心线程,即90个非核心线程,此时有1000个任务进来,线程池中只有10个线程在工作,即只有核心线程在工作,非核心线程需要两个条件都满足时才会被创建:1:核心线程满 2:仓库满,两个条件缺一不可,所以只有10个线程在工作)
- ScheduledExecutorService:加入了定时任务的支持(Eurka心跳检查就用到了它)
- Executors:存在陷阱,应为它创建的线程池的仓库都是无界的,即有多少并发就可存放多少任务,但是线程是有限的,当并发特别大时,如上万并发,阻塞非常严重了,甚至任务一放进仓库时就直接报错