文章目录
1. 线程与进程
进程(process):是程序的一次执行过程。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。(进程是动态的)是一个动的过程 ,进程的生命周期 : 由它自身的产生、存在和消亡的过程
线程(thread): 进程可进一步细化为线程, 是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程的。
2. 并发与并行
并发:单核cpu运行多线程时,时间片进行很快的切换。线程轮流执行cpu
并行:多核cpu运行 多线程时,真正的在同一时刻运行
3. 线程的三种创建方式
继承Thread类
start与run方法的区别:
start方法的作用:1.启动当前线程 2.调用当前线程的重写的run方法(在主线程中生成子线程,有两条线程)
调用start方法以后,一条路径代表一个线程,同时执行两线程时,因为时间片的轮换,所以执行过程随机分配,且一个线程对象只能调用一次start方法。
run方法的作用:在主线程中调用以后,直接在主线程一条线程中执行了该线程中run的方法。(调用线程中的run方法,只调用run方法,并不新开线程)
总结:我们不能通过run方法来新开一个线程,只能调用线程中重写的run方法(可以在线程中不断的调用run方法,但是不能开启子线程,即不能同时干几件事),start是开启线程,再调用方法(即默认开启一次线程,调用一次run方法,可以同时执行几件事)
实现Runnable接口
实现Callable接口
总结
4. 线程的生命周期
线程的生命周期:
JDK中用Thread.State类定义了线程的几种状态,如下:
-
新建 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
-
就绪 处于新建状态的线程被start后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
-
运行 当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能
-
阻塞 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态
-
死亡 线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
5. 线程池
5.1 为什么需要使用线程池
想想我们之前没用线程池的时候,每次创建线程都是:new Thread(() -> {...})
,再调start()
方法来执行线程。这就会带来一系列问题,比如:线程的创建和销毁都是很耗时很浪费性能的操作。再者,简单的new两三个Thread还好,但若需要上百个线程呢?而且用完再销毁掉时又要一个一个的进行销毁。那这上百个线程的创建和销毁的性能是很糟糕的!
线程池诞生就是为了解决上述问题。其核心思想就是:线程复用。也就是说线程用完后不销毁,放到池子里等着新任务的到来,反复利用N个线程来执行所有新老任务。这带来的开销只会是那N个线程的创建,而不是每来一个请求都带来一个线程的从生到死的过程。
因此使用线程池的好处与优点:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
- 提高线程的可管理性。使用线程池可以对线程进行统一的分配,调优和监控。
5.2 创建线程池
-
newSingleThreadExecutor
:一个单线程的线程池,可以用于需要保证顺序执行的场景,并且只有一个线程在执行。 -
newFixedThreadPool
:一个固定大小的线程池,可以用于已知并发压力的情况下,对线程数做限制。 -
newCachedThreadPool
:一个可以无限扩大的线程池,比较适合处理执行时间比较小的任务。 -
newScheduledThreadPool
:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。 -
newWorkStealingPool
:一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。
public ThreadPoolExecutor