多线程的好处
- 发挥多核优势,多核多线程可同时执行多个任务
- 防止阻塞,防止一个线程IO时间过长,浪费CPU
- 便于建模,可将一个大任务分成多个小任务,减少执行时间
创建多线程的方式
- 继承Thread类
重写run()方法,将具体执行逻辑写入run()中 - 实现Runnable接口
实现run()方法,将实现类对象作为参数传到Thread类的构造器中,创建Thread类的对象 - 实现Callable接口
(1) 实现Callable接口,实现call()方法,将执行逻辑写到call中,创建实现类对 象,作为参数传入FutureTask的构造器中,创建FutureTask的对象,将FutureTask的对象作为参数创建Thread对象
(2)(可选)获取Callable中call方法的返回值
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
- call()可以有返回值
- call()可以抛出异常被外面捕获,从而获取异常信息
- Callable是支持泛型的
线程池
线程池的好处?
- 减少频繁创建,销毁线程带来的开销问题
- 提高响应速度,部分情况可以直接使用不用等待创建
- 可以规范线程的创建回收,防止线程的无限制创建,同时不用担心忘记销毁
线程池组成部分?
- 线程池管理器:创建并管理线程池,包括创建销毁线程池,添加新任务
- 工作线程:工作池中的线程,可以循环的执行任务
- 任务接口:每个任务必须实现的接口
- 任务队列:缓冲没有处理的任务
线程池两种创建方式?
Executors:
- ScheduledThreadPool:创建一个可定时执行任务的线程池
- FixedThreadPool:创建一个固定大小的线程池,可控制并发线程数,超出的队列等待
- SingleThreadExecutor:创建单个线程数的线程池,可保证先进先出的执行顺序
- CachedThreadPool:创建一个可缓存的线程池
- SingleThreadScheduledExecutor:创建一个单线程的可定时执行任务的线程池。
- WorkStealingPool:创建一个抢占式执行的线程池
主线程异常,线程池中的线程会不会回滚?
不会回滚,每个子线程都有自己的ThreadLocal,ThreadLocal中存着connection,所以他们的事务互不干扰
ThreadPoolExecutor:
参数: 核心线程数,最大线程数,线程空闲时间,时间单位,任务队列,创建线程池中工作线程的工厂,拒绝策略
阿里规范: 线程池一定要使用ThreadPoolExecutor创建,这样可以更加明确线程池的运行规则,规避资源耗尽的风险
Executors线程池对象的弊端:
- FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量请求,最后OOM
- CachedThreadPool: 允许创建线程的数量为Integer.MAX_VALUE,可能会创建大量线程,最后OOM
多线程,线程数量如何决定?
场景区分,IO密集型,CPU密集型
CPU密集型,如何设置线程数?
- 如果单核CPU,CPU密集并不适用于多线程
- 如果多核CPU,CPU密集的话,可以设置CPU核数+1的线程数,+1是为了防止某时因为一个页错误或其他原因而发生CPU暂停,这时有一个额外线程保证这种情况下CPU周期不会中断工作。
IO密集型,如何设置线程数?
公式: 线程数 = IO耗时/CPU耗时 + 1
公式终究是公式,具体可以做个参考,初始值,后面可以根据实际的CPU,内存,硬盘,网络等逐步调优
如何知道准确IO耗时与CPU耗时?
可以使用一些APM工具,如SkyWalking,CAT,zipkin等
线程数设置也要参考实际服务器的核心数,如果只有10个核心,公式计算的结果是需要设置80个线程,那么明显是不合理的,因为我们要考虑到线程切换的开销
多线程,异步,消息队列的比较
待补充
借鉴博客:
https://blog.csdn.net/siyuanwai/article/details/119306680
https://blog.csdn.net/qq_39232813/article/details/123543277
https://blog.csdn.net/weixin_39771749/article/details/123576797
https://blog.csdn.net/jiey0407/article/details/125190643