Java多线程编程核心技术-----第三章读书笔记
3.1.3
调用wait()或notify()等方法的对象,必须是同步锁对象,否则抛出IllegalMonitorStateException.
比如同步方法中只能是 this.lock()。
wait()使当前执行代码的线程进入等待状态(冷冻暂停),并释放同步锁。
sleep方法不释放同步锁。
调用wait()方法时必须要持有同步锁,即在同步代码中才能正确调用wait(),否则抛出IllegalMonitorStateException。
wait()是Object类的方法。
notify()随机使得一个wait状态的线程恢复执行。
notify()需要在同步代码中执行,此线程执行完才释放同步锁。然后被唤醒的线程才有机会去争抢同步锁。
当没有wait状态的线程,那么就忽略本次操作。
wait状态的线程必须要得到notify()或者notifyAll()才能被唤醒,否则一直处于wait状态。
notifyAll()方法唤醒所有wait状态的线程。
原:wait使线程停止运行,而notify使停止的线程继续执行。P137
P139例子用意: lock.wait();//释放了同步锁,另外一个线程 lock.notify();//唤醒其它线程,但不立即释放同步锁
wait()/notify() 要结合 Synchronized 关键字一起使用,因为他们都需要首先获取该对象的对象锁;
出现线程等待的原因:
调用sleep主动放弃CPU时间片
其它线程争抢到了同步锁
调用了wait()方法进入wait状态
线程重新进入可执行状态的情况:
sleep()方法的睡眠时间到了
线程抢夺到了同步锁
线程被notify()唤醒了
另外就是IO阻塞等待用户输入(输入完毕)
suspend挂起(使用resume恢复)
3.1.5
wait状态的线程需要使用try catch(InterruptedException e) 进行对线程的中断状态位进行监控。
一旦此线程的中断状态位被置为true,那么则进入异常处理语句,并中断此线程,并清除状态位。
3.1.8
wait(long)方法功能是:等待某一时间内是否有线程对其唤醒,超过此时间就自动唤醒。
此方法释放同步锁,相当于一个释放锁版的sleep方法,当然也可以在时间范围内提前唤醒它。
3.1.11
生产者:如果产品生产出来了,等待消费者消费。否则生产并通知消费者消费。
消费者:如果产品没有生产,那么等待生产者生产。消费完,则通知生产者生产。
伪代码:
生产者 if(有产品){lock.wait} 生产 lock.notify 如果有产品,我就休息。不然进行生产,并通知消费者消费。
消费者 if(没产品){lock.wait} 消费 lock.notify 如果没有产品,我就等待。不然的话就消费,消费完就通知生产。
抓住两者什么时候wait,什么是notify。
生产者wait说明有货了,消费者wait说明没货了。主体代码是body,意外情况在前面用if语句加上。
生产者生产完notify消费者去消费,消费者消费完notify生产者生产。
总之,先wait后notify的写法是很好的,符合先判断不好的情况。
多生产多消费出现假死。
ABCD线程都刚开始都处于 可执行状态,AB是生产者,CD是消费者。
A刚开始生产了以后,又抢到了锁,因为有货,那就wait。
操作 结果 剩余线程
A生产 (有货) (ABCD还醒)
A运行,A等待。(有货) (BCD)
B运行,B等待。(有货) (CD)
C消费,唤醒A。(无货) (ACD)
C运行,C等待。(无货) (AD)
D运行,D等待。(无货) (A)
A生产,唤醒B。(有货) (AB)
A运行,A等待。(有货) (B)
B运行,B等待。(有货) (全睡了)
从上述的例子来看,C唤醒了D,然后是A唤醒了B,出现连续唤醒同类,导致的假死。
原:假死出现的主要原因是有可能连续唤醒同类,解决办法是将异类也一同唤醒。P165
把唤醒一个的notify方法修改为notifyAll方法即可。
原:因为条件发生改变时并没有得到及时响应。
采用while替代if,消费者A消费完唤醒消费者B,消费者B应该重新判断是否有货。
多个生产者或者多个消费者时,把if换为while,并把唤醒换为notifyAll。
为什么要换位while?因为比如消费者A消费完了货再唤醒其它线程。此时条件已经变化了,货物已经被消费。
所以要让新唤醒的消费者再去重新判断一下条件,如果没货,继续wait。如果不是while而是if的话,那么新唤醒的消费者
则顺序调用消费语句,很可能出现诸如空指针异常等异常。
notifyAll为了避免出现连续唤醒同类,所导致假死的状况,保证能够唤醒异类。
3.2.1
A线程的任务中出现 B.join(); 那么A线程需要等待B线程任务执行完毕再继续执行。
3.2.2
join方法内部采用wait()方法实现。
线程A调用 otherThread.join()方法,那么线程A进入 WAITING 状态,内部使用的是 wait()方法实现,所以会释放同步锁。
3.2.3
如果a线程在b.join的过程中,调用的线程a要等待另外一个线程b执行完毕,所以处于wait等待状态。
如果调用的线程a的中断状态位被设置为true,那么a会出现异常。但是不会影响到b线程的执行。
3.2.5
this.join(long),源码是this.wait(long),能够调用wait方法的是同步锁,所以this是同步锁。
也就是抢到了锁,然后又释放了锁,指定时间后自动苏醒。
3.3.1
原:ThreadLocal实现了每个线程内部都有自己的共享变量。P191
ThreadLocal实现原理:ThreadLocal相当于一个Map<Thread,Object> map对象。键的话,就是currentThread() ,
值的话,就是你存进去的那个值。
SimpleDateFormat是线程不安全的,解决方法是为每个线程创建一个sdf对象,而不是共享。
/**
* 获取simpleDateFormat对象,线程安全
*
* @return 返回当前线程绑定的simpleDateFormat对象
*/
public static SimpleDateFormat getSimpleDateFormat() {
//创建格式器引用
SimpleDateFormat sdf;
//先拿一次看看,private static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<>();
sdf = t1.get();
if (null == sdf) {
//如果是空,说明当前线程第一次调用此方法
//构造一个SimpleDateFormat对象
sdf = new SimpleDateFormat();
//将新创建的sdf放入线程私有变量
t1.set(sdf);
}
return sdf;
}
3.3.3
继承ThreadLocal类,重写initialValue()方法。创建此MyThreadLocal类对象,调用get()返回值就是此键值对的初始值。
3.4.1
使用InheritableThreadLocal可以让子线程从父线程的键值对中取得值。
A线程启动了B线程,那么A线程是B线程的父线程。
也就是A线程中的值,子线程B也能获取到A线程中的键值对的值,而不是重新计算表达式。
3.4.2
通过重写InheritableThreadLocal的childValue(parentValue)方法,重新定义MyInheritableThreadLocal的键值对。
A线程启动了B线程,那么A线程是父线程,B线程是A线程的子线程。
B线程此刻访问自己的键值对的时候,拿的是父类的键值对的值。
当然,如果重写了childValue()方法,那么拿的就是自己的值了。
-----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------
消费者生产者 经典代码。
package com.ssi.domains.Add;
/**
* Created by jay.zhou on 2018/7/24.
*/
public class Product {
public String state = "无货";
public void create() {
state = "有货";
}
public void consume() {
state = "无货";
}
public static void main(String[] args) {
Product product = new Product();
Object lock = new Object();
ThreadA threadA = new ThreadA(product,lock);
ThreadB threadb = new ThreadB(product,lock);
threadA.start();
threadb.start();
}
}
class ThreadA extends Thread {
private Product product;
private Object lock;
public ThreadA(Product product, Object lock) {
this.product = product;
this.lock = lock;
}
//生产者线程
@Override
public void run() {
while (true) {
try {
synchronized (lock) {
if (product.state.equals("有货")) {
lock.wait();
}
product.create();
System.out.println("生产者生产出货物");
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadB extends Thread {
private Product product;
private Object lock;
public ThreadB(Product product, Object lock) {
this.product = product;
this.lock = lock;
}
//消费者线程
@Override
public void run() {
while (true) {
try {
synchronized (lock) {
if (product.state.equals("无货")) {
lock.wait();
}
product.consume();
System.out.println("消费者消费完货物");
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}