1.线程的状态:
NEW: 安排了工作, 还未开始行动
RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
BLOCKED: 这几个都表示排队等着其他事情
WAITING: 这几个都表示排队等着其他事情
TIMED_WAITING: 这几个都表示排队等着其他事情
TERMINATED: 工作完成了
如t开始之前,得到的就是NEW,t正在工作,得到的是runnable,t结束之后,得到的状态是TERMINATED
2.线程安全:抢占式执行造成
用加锁操作synchronized来解决,使原先的LOAD ADD SAVE操作外多了LOCK和UNLOCK
原本两组线程调度是随机的,一旦两组load add save交织在一起,就会产生线程安全问题,而使用锁就可以使这两个线程串行执行了。
任意对象都可以在synchronized里面作为锁对象
public static Locker locker=new Locker();
public void increase(){
synchronized(locker){
count++;
}
}
或
public void increase(){
synchronized(this){
count++;
}
}
(synchronized里面写的锁对象使this)谁调用increase,就是针对谁进行加锁
两个线程对同一对象加锁,就会出现互斥。
synchronized特性:互斥和可重入(锁上加锁)
内存可见性也会引发安全问题:
在编译器优化的背景下,一个线程把内存给改了,另一个线程并不能及时的感知到
解决方法:避免让编译器做出‘不读内存’的优化
故使用volatile“可变的” 用这个关键字来修饰一个变量,此时被修饰的变量,编译器就不会做出“不读内存,只读寄存器”这样的优化
volatile保证内存可见性:一个线程读,一个线程修改这个场景(两个线程一块修改是无能为力的)但volitale不保证原子性
wait操作的作用:
1.释放当前锁
2.进行等待通知
3.满足一定条件的时候(别人调用notify),被唤醒,然后尝试重新唤醒锁
(wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常)
t1 加锁后如果wait 则会顺带附加解锁效果(t2原先想加锁但t1在用锁故阻塞,但t1wait后t2就可以加锁了),直到t2notify之后,t1再被重新加锁直到其也notify释放。
加锁的对象和调用wait的对象得是同一个对象,还要保证,调用wait的对象和调用notify的对象也是同一个对象(a.wait()是无法被b.notify()唤醒的)