线程状态转移
状态转移注意点
- 线程调度会发生在任意时间上。
- 加锁是个过程:申请加锁到枷锁成功,中间间隔时间会很长。所以,申请加锁之前的很多情况会发生变化。
- 只有在线程状态是RUNNABLE时,才可以资格竞争CPU,才可以抢锁。
- 状态不是RUNNABLE,必然会放弃CPU,也没资格抢CPU;但是,状态时RUNNABLE,不代表一定可以占有CPU。
- 那些场景线程会放弃CPU?
放弃CPU的场景 |
当前状态 |
抢锁失败 |
BLOCKED |
wait() |
WAITING |
sleep(long) |
TIMED_WAUTING |
时间片到了 |
RUNNABLE,进入就绪队列 |
yeild |
RUNNABLE,进入就绪队列 |
运行结束 |
TERMINATED |
线程不安全
- 线程之间进行了共享变量的操作(读/写)
1>除了栈,其他区域里的都是共享的。
2>共享不一定就是不安全的(如果没有线程之间相互操作,也不会不安全)。
3>ThreadLocal
线程安全(给每个线程划分自己的空间)。
- 共享变量只读(不写),一般不考虑线程安全问题。
- 原子性/可见性/重排序
强调:机制
- synchronized:原子性/可见性/重排序正确性
注意:主要发生进程调度,成本就很高(尽可能设计无锁队列)
- volatile 可见性、重排序(对象的初始化)
- wait / notify:使用前加锁、过程会释放锁(释放wait对象的那把锁)
死锁、活锁
- 死锁
1> 定义:广义上的(持有锁的人,停在某个环节,不会释放锁)。
eg:请求其他锁/wait/死循环
2> 注意:出现死锁,可用 jconsole,观察每个线程的状态+调用栈
- 锁的可重入性
理解:一个锁住的锁,是否允许拥有这把锁的进程?(如果允许,具有可重入性;否则不具有)
eg:synchronized
具有锁的可重入性。
synchronized (this){
synchronized (this){
}
}