导航
多线程
什么是死锁?如何避免死锁?
死锁就是两个线程相互等待对方释放锁。
产生死锁的四个必要条件:
- 资源是独占的(互斥使用),一个线程获得该资源的时候,其他线程不可占有。
- 非抢占式的,不能被抢占,只能等资源拥有者主动释放
- 请求和保持,拥有资源的对象取请求其他资源的时候,保留自己的资源
- 资源的请求形成闭合的环路(重要)
如何避免
打破四个必要条件之一即可
JVM 是单线程的吗?
虽然一个简单的Java程序( Hellow Java)只会创建一个线程,但是 JVM 不会单线程的,因为 JVM 还会创建其他的线程,例如 垃圾回收(gc) 线程。
Thread 中 start 方法 和 run 方法的区别
- 如果直接调用 run方法,会当成 Thread 对象的普通方法使用。
- 而调用 start 方法,会让 JVM 新建一个线程,并运行其中的 run 方法
Thread 和 Runable 是什么关系?
Thread 实现了 Runable 接口,使得 Runable 接口中的 run 方法实现多线程。
由于类的单继承特性,推荐使用 Runnable 接口
(Java 中类是单继承的,不过接口却可以多继承)
如何给 run 方法传参
- 主线程等待法
- 使用Thread类的 join() 方法 阻塞当前线程,并等待子线程的完成
- 使用 Callable 接口实现: 通过FutureTask 或者 线程池获取
Java 中线程的状态
新建状态(NEW): 创建了但是还没有启动
- 还未使用start方法
运行(Runnable): 包含Running和Ready
- 等待CPU分配时间片,在两种状态中转换
无限期等待(Waiting): 不会被分配CPU执行时间,需要显式唤醒
- join 方法(未给定超时时间)
限期等待(Timed Waiting): 在一段时间以后,由系统自动唤醒
- Thread 的 sleep 方法
阻塞(Blocked): 等待获取排它锁
- synchronized 等待锁的释放
结束(Terminated): 以结束线程的状态,线程已经结束运行了
- 已结束的线程不能再被启动了
sleep 和 wait 的区别
基本的区别
- sleep是Thread类的方法,wait是Object类中定义的方法
- sleep() 方法可以在任何地方使用
- wait() 方法只能在synchronized方法或者synchronized块中使用
最本质的区别
- Thread.sleep 仅让出CPU,不会导致锁行为发生改变
- Object.wait 不仅让出CPU,还会释放已经占有的同步资源锁
正是由于 wait 方法要让出同步资源锁,所以它需要在 同步块或方法中使用
同时,如果wait方法如果没有加超时时间,就会先入无限期等待,否则是限期等待
notify和notifyAll的区别
- notifyAll 会让所有处于等待池中的线程全部进入锁池去竞争获取锁的机会
- 进入了锁池的线程不会再回到等待池中
- notify 只会随机选取一个处于等待池中的线程进入锁池中去竞争获取锁的机会
锁池:如果一个线程试图获取某个同步块的锁时,会进入锁池中
等待池:如果一个拥有锁的线程,调用了wait方法,就会进入等待池中
yield
当调用 Thread.yield() 函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示。
》 调用 yield 并不会影响锁的状态
如何中断线程
调用interrupt(),通知线程应该中断了
- 如果线程处于堵塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常
- 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续进行,不受影响
需要被调用的线程配合中断:在正常活动状态,经常检查本线程的中断位,如果被设置了中断标志就自动停止线程