线程概述
线程和进程
- 进程:程序的一次执行过程,程序的运行就是一个进程从创建、运行到消亡的过程。进程是计算机运行的基本单位,进程之间是相互独立的。
- 线程:线程是 CPU 执行的基本单位,一个进程可以分为多个线程,多个线程对进程的资源使用存在竞争关系。
线程的状态
- 创建:线程 new 之后进入创建状态,调用 start() 方法进入就绪状态。
- 就绪:线程就绪状态需要等待 CPU 调用执行
- 运行:线程获取了 CPU 时间,进入运行中状态
- 等待:wait、join、LockSupport.park 进入等待状态,需要其他线程通知才能进入运行状态 (notify、notifyAll、LockSupport.unPark)
- 超时等待:在等待的基础上加了一个超时时间,超时时间到了就自动进入运行状态(sleep,wait(long),LockSupport.pariUntil,LockSupport.parkNanos)
- 阻塞:线程调用同步方法或者代码块,未获得锁时,进入阻塞状态
- 终止:线程执行完 run 方法后进入终止状态
并行和并发
- 并行:多个处理器上,执行多个任务
- 并发:单个处理器上,多个任务交替执行。线程通过轮询获取 CPU 的执行时间。
线程死锁
- 多个线程同时被阻塞,他们中的一个或者多个都在等待某个资源被释放。由于无限期的阻塞,线程无法正常终止。
线程死锁的四个必要条件
- 互斥条件:某一个资源某一时刻只能有一个线程使用
- 请求和保持条件:一个线程在请求其他资源的时候,对其中持有的资源不释放
- 不可剥夺条件:线程已获得的资源在未使用的情况下不能被其他线程强行剥夺,只能由该线程使用完毕后自己释放
- 循环等待条件:多个线程形成的一种首位相接的等待资源的关系。
避免死锁
破坏线程死锁产生的四个条件之一
- 破坏互斥条件:这个条件我们没法破坏,因为我们用锁本来就是想让他们互斥(临界资源需要互斥访问)
- 破坏请求和保持条件:一次性申请所有的资源。
- 破坏不可剥夺条件:占用部分资源的现场进一步申请其他资源时,如果申请不到,可以主动释放他自己占有的资源。
- 破坏循环等待条件:靠按序申请资源来预防,按某一顺序申请资源,释放资源则反序释放。
sleep和yeild方法
- sleep方法在超时等待一定时间后会自动唤醒,进入就绪状态;yield方法是当前线程进入就绪状态线程调
- sleep方法后,无论其他线程的优先级都有机会运行;执行yield方法,只会给相同优先级或更高优先级的方法的线程运行的机会
- sleep方法会抛出InterruptedException异常,而yield方法没有任何异常声明
- 循环中调用yield方法,yield死循环:线程yield之后有获得了cpu,导致其他线程无法获取资源
调用start方法,为什么不直接调用run方法
- 调用 start 方法方可启动线程并使线程进入就绪状态,
- 而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。
线程创建的方式
- 继承Thread类
- 实现Runable接口,实现run方法
- 实现Callable接口,实现call方法
- 使用线程池