这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战
线程通信
- 线程通信的目的是使线程之间能够相互发送信号进行通信,同时线程信号能等待来自其他信号的状态。例如线程a接收到线程b的信号,得知线程b所操作数据已经准备好等待下一步处理。
发送线程信号的方式
通过共享变量值进行线程通信
- 将
status作为共享变量,a与b两个线程通过synchronized修饰的方法对变量值进行修改与查询。当变量值发生变化时,即检测到其他线程的完成状态,执行线程后续操作。
private boolean status = false;
public synchronized boolean isStatus() {
return status;
}
public synchronized void setStatus(boolean status) {
this.status = status;
}
- 要注意的是,这种方法需要保证不同线程所采用的共享变量实例为同一个。
线程共享变量值时的忙等待
while(isStatus()){
//do something
}
-当线程a等待线程b将status的过程成为线程的忙等待。要注意的是,线程a需要使用while语句而不是if语句,保证当数据变化后能够正确的进行执行。否则由于时间片切换的原因会导致线程aif中的语句不会执行。
通过wait()、notify()、notifyAll()等内置方法进行通信
- 线程等待的方式是采用cpu时间片切换时进行判断的方式,这并非是对cpu的高效利用。如果能够采用类似于发布订阅的方式,当线程执行时才唤醒是一个比较理想的通信方式。
- 而java提供了这种类似的机制,允许线程在等待时变为非活动变量。为了实现这个机制,
java.lang.Object提供了wait()、notify()、notifyAll()三个方法。 - 线程a调用wait方法可以使线程进入等待状态,直到其他线程调用notify方法变为活跃状态。而这一切的前提是线程首先获得该对象的锁。也就是wait方法和notify方法必须在
synchronized代码块中执行才能生效,线程直接执行会产生异常提示,IllegalMonitorStateException。而notifyAll方法则是会唤醒所有线程的等待状态,形成争抢。 - 至于为什么一定要在
synchronized代码块中执行,主要是为了线程执行顺序的保证。倘若两个线程不加synchronized,很有可能notify代码先于wait代码执行,而这会导致wait代码永远无法被唤醒。
虚假唤醒
- 虚假唤醒主要是只线程在本不该唤醒时被唤醒执行产生异常。
- 虚假唤醒想要避免主要通过采用while替代if进行锁等待等操作。
- while能避免虚假唤醒的原因:
// 使用if
if(a == 1){
//1.等待执行
lock.wait()
//2.执行无关逻辑
// 打印
}
//3.执行逻辑
// do something
//4.通知其他线程
lock.notifyAll()
//使用while
while(a == 1){
//1.等待执行
lock.wait()
//2.执行无关逻辑
// 打印
}
//3.执行逻辑==由于增加了while判断执行不到
// do something
//4.通知其他线程==由于增加了while判断执行不到
lock.notifyAll()
- 如上代码可知,通过while的二次判断,可以在虚假唤醒时避免相关的异常。
后记
- 千古兴亡多少事?悠悠。不尽长江滚滚流。