为什么要使用多线程呢?
好比是马拉车,你明明有多匹马,但是你因为不会让多匹马拉一辆车,而天天只玩一匹马拉车。是多匹马并驾一起拉车好,还是一匹马好呢?
之前条件艰苦,没有那么多马(单核CPU),现在不一样了,随着计算机硬件发展,处理器都是多核的了。
目录
第一个问题,实现多线程的方式有几种?分别是?
第二个问题,既然有多种实现方式,怎么选择?有什么区别?
第三个问题,Thread的构造方法没有传Callable参数进去的,全部都是传runnable进去的,怎么办?
第四个问题,为什么要使用线程池呢?
第五个问题,线程池怎么使用的?
第六个问题: 线程池的底层原理
第七个问题,四个拒绝策略
第八个问题,在实际应用中如何使用线程池
最后一个问题,如何合理配置线程数?
第一个问题,实现多线程的方式有几种?分别是?- 继承Thread类。这个肯定不会用的,java是单继承的,万一类需要继承其它类,又要继承Thread,显然是可能的。同时也不符合依赖倒置原则,面向接口编程的思想。
- 实现 Runnable接口。
- 实现 Callable接口。
- 使用线程池
- 同样是实现接口,Runnable和Callable又有什么区别呢?Callable可以有返回值,可以抛异常。在实际找错的时候,能够抛异常和带返回值还是很重要的,
- 为什么有了Runnable还要有Callable呢? 并发
- 找一个同时实现了Runnable接口和Callable接口的中间类。这是接口适配的精髓,需要注意,在JDK的很多的设计,都用到这个思想。
注意上边的 get方法尽量往后放,否则会被阻塞。就违背了多个线程的意义。
第四个问题,为什么要使用线程池呢?
前边回答过,想要使用多线程,我们还可以使用线程池,其实这就是池的思想了。就像数据库连接池一样。
- 首先,我们在创建线程就肯定要花费时间,线程用完需要销毁回收,也需要时间。我们使用池的思想,根据我们的需求,提前创建一些线程放在池中,这样就不用每次都去创建和销毁了。并且我们可以去对池做优化。
- 有了池以后,会方便我们对线程的管理。
- 线程池是这样来使用线程的,不需要new,使用提供好的Executors辅助工具类
第五个问题,线程池怎么使用的?
- 一个线程池固定个数的线程数,使用如下:
那么工具类提供的三种形式分别什么时候用呢:
需要特别的记住这个辅助工具类,Executors。工具辅助类所提供的三种方式底层源码都是TreadRoolExecutor,区别是里边的阻塞队列使用的不太一样。
- 线程池的7个参数问题
- 线程池底层工作原理
讲一遍:任务上来以后,先执行,如果有需要等待的就去阻塞队列排队,队也排满了以后就去叫帮忙干活的线程,如果还满,就到第四步拒绝了。拒绝测咯分四种。
第八个问题,在实际应用中如何使用线程池
首先,上Executor辅助工具类提供的三种方式是都不能使用的。原因是无界阻塞队列在实际生产线上一定会带来很大的问题。
在阿里巴巴的开发手册中明确指明:线程池不允许使用Executor提供的。
原因是:
手写一个线程池:
分两种情况,一种是CPU密集型的(大量运算),一种是IO密集型的(大量的输入输出)
- CPU密集型:硬件的CPU的核数+1
- IO密集型的:因为不是一直在运算,尽可能多的配置。推荐是 硬件的CPU内核数*2
- IO密集型的还有一种方法:核数 / (1-阻塞系数)