线程通信相关知识点
关于对象中的等待集(wait set)
Object.wait()
Object.notify()
Object.notifyAll()
用来做线程通信的
Java中每个对象:等待集
1.wait会使线程状态发生变化 RUNNABLE - > WAITING
WAITING - > RUNNABLE
2.等的是对象的等待集上(wait是属于object的一个方法,所以等待在该对象的等待集上)
3.使用的时候必须加锁,等在哪个对象上,就对哪个对象加锁,wait执行成功时会释放锁,醒来时会重新请求锁
4.哪个线程调用wait,哪个线程进入等待集
5.notify只唤醒一个,但不保证是哪一个
v1和v2会cpu空转
v3条件不满足,立即出现cpu调度,但正确的线程被调度上来的机会低
v4条件不满足,立即出现cpu调度,并且指示自己不在参与调度了
1.wait和notify和notifyAll
1.线程状态的问题
2.加锁的问题(等在哪个对象,加哪把锁)
2.按序打印one two three
四个版本,越来越好
3.生产者消费者模型
1.加入线程安全
2.提升效率
1.阻塞队列
2.定时器
3.线程池
定时器:
execute() {
把任务带着时间放到一个优先级队列中(带阻塞的)
}
工作线程 {
while(true){
从优先级队列中取任务(可能会阻塞)
if(task.delay如果还没到时间) {等待相应时间}
else{去执行};
}
}
哪些场景下,线程会放弃CPU
1.抢锁失败 =》 BLOCKED
2.wait =》 WAITING
2.1 sleep =》 TIME_WAITING
3.时间片到 =》 RUNNABLE
4.yield =》 RUNNABLE
5.运行结束 =》 TERMITATE
状态不是RUNNABLE,必然放弃CPU,也没资格抢CPU
但是,RUNNABLE不代表就占有CPU
线程不安全
1.线程之间进行共享变量的操作了(读/写)
1)哪些区域的变量是共享的
2)哪怕有堆里的变量,如果没有线程相互操作,也不会不安全
3)ThreadLocal
2.共享变量之间,如果只是只读,没有写,一般不需要考虑
3.原子性/可见性/重排序
机制:
synchronized 原子性/可见性/重排序正确性
只有发生线程调度,成本就很高(无锁编程)
volatile 可见性,重排序(对象的初始化)
wait/notify 加锁,过程中会释放锁(那把锁?)
难点:
1.调度会发生在任意时间上的
2.加锁是个过程:申请加锁到成功加锁,中间的间隔时间会很长,所以,申请加锁之前的很多情况都会发生变化
额外:
1.死锁 广义上的(持有锁的人,停在某个环节上,不会释放锁了)
请求其他锁/wait/死循环
jconsole,观察每个线程的状态+调用栈
2.锁的可重入性
一个锁住的锁,是否允许拥有这把锁的线程重新加锁
如果允许:具备可重入性,否则就不具备
synchronized(this) { synchronized具备
sunchronized(this) {
}
}