线程的出生
众所周知,计算机可以“同时”
运行多个进程来执行不同的任务(操作系统通过CPU时间片不断切换执行从而达到宏观上的同时运行。单核CPU在任意一个CPU时间片只会有一个进程在执行),那为什么还需要发明个线程呢?
- 在多核 CPU 中,利用多线程可以实现真正意义上的并行执行
- 在一个应用进程中也会存在多个同时执行的任务,若其中任意任务被阻塞,按原先的方式则会导致其他任务也会被阻塞。 通过对不同任务创建不同的线程去处理,可以提升程序处理的效率
- 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程的代价更小
线程的创建
继承Thread类
Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。
启动线程的唯一方法就是通过 Thread 类的 start()实例方法。
start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法
1 | public class MyThread extends Thread { |
实现Runnable/Callable
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable 接口。
1 | class MyThread extends OtherClass implements Runnable { |
线程池的创建
线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。
有返回值的任务必须实现 Callable 接口,类似的,无返回值的任务必须 Runnable 接口。
执行Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务返回的 Object 了,再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程了
1 | //创建一个线程池 |
线程的生命周期
线程既然能够创建,那么也能被销毁,所以线程是有生命周期的。
线程一共有 6 种状态(NEW、 RUNNABLE、 BLOCKED、WAITING、 TIME_WAITING、 TERMINATED)
NEW
-初始状态- 线程被构建此时还未调用
start
方法
- 线程被构建此时还未调用
RUNNABLED
-运行状态- 即操作系统中的
就绪
和运行
两种状态
- 即操作系统中的
BLOCKED
-阻塞状态- 线程因为某种原因放弃了CPU使用权进入等待状态,阻塞也分为几种情况
- 等待阻塞
- 运行的线程执行
wait
方法, jvm会把当前线程放入到等待队列
- 运行的线程执行
- 同步阻塞
- 运行的线程在获取对象的同步锁时,若该锁被占用了,则jvm会把当前线程放入到锁池中
- 其他阻塞
- 运行的线程执行
Thread.sleep
/join
方法或者有I/O请求时,jvm会把当前线程设置为阻塞状态,当 sleep结束/join线程终止/io处理完毕则线程进入RUNNABLED
- 运行的线程执行
- 等待阻塞
- 线程因为某种原因放弃了CPU使用权进入等待状态,阻塞也分为几种情况
TIME_WAITING
-超时等待状态- 超时以后自动返回
TERMINATED
-终止状态- 表示当前线程执行完毕
线程的生命周期图例