wait/notify/synchronized
线程等待/通知机制实现了多个线程基于特定条件的通信机制。
线程等待/通知机制必须配合线程同步机制避免资源竞争(Race Condition)产生的数据异常。
场景
一、等待-通知机制细节(为什么需要引入同步机制?)
- 第一个thread验证通信条件未通知需要调用wait()
- 第二个thread改变通信条件
- 第二个thread调用notify(),此时第一个thread并未调用wait
- 第一个thread调用了wait()
此种情况是在没有加入同步机制的情况,如果不引入同步机制那么thread#1的处理逻辑就不够完备。
在Java中使用wait()/notify()时约束必须在synchronized块中调用。
二、thread收到通知(通信条件为什么必须重新检验?)
- Thread#1调用取得同步lock的method
- Thread#1检验通信条件不是所需的状态
- Thread#1调用wait()并释放lock
- Thread#2调用取得同一个lock的method
- Thread#3调用同步lock的method,但因为lock已被Thread#2占有并未释放,所以Thread#3是阻塞状态
- Thread#2改变通信条件并调用notifyAll
- Thread#2逻辑处理完成,释放lock
- Thread#3取得lock
- Thread#3验证通信条件,此时通信条件是所需的状态
- Thread#3进行逻辑处理并将通信条件重置
- Thread#3逻辑处理完成,释放lock
- Thread#1收到通知,此时通信条件不是所需的状态应该再次调用wait
Thread#1因为通信条件不成立而调用wait(),一段时间被调用notifyAll()的线程唤醒,但此时通信条件在notifyAll调用后与Thread#1被唤醒前可能已经改变,所以就要求Thread#1必须在唤醒后重新检查通信条件。
notify()/notifyAll()
当有多个thread在等待无法确定具体哪一个thread会在线程调用notify()后收到通知,这种状况与多个因素有关(JVM的实现、程序运行时调度)。
为什么会需要notifyAll(),这与通信条件有关,当有多个各自通信条件不相同的线程在等待,此时因为无法确认调用notify()之后到底哪个线程会被唤醒(并且当前通信条件并不能满足被唤醒线程自身所需的通信条件),所以通过notifyAll()唤醒所有thread。
示例
1.等待-通知与sychronized
package org.ybygjy.thread;
/**
* 等待-通知
* @author WangYanCheng
* @version 2012-11-18
*/
public class WaitAndNotify {
// TODO 一个线程负责生产
// TODO 二个线程负责消费
// TODO 一个线程负责消费%2==0
// TODO 一个线程负责消费%2==1
public static void main(String[] args) {
VariableQueue vqInst = new VariableQueue();
new Producer(vqInst).start();
new Consumer1(vqInst).start();
new Consumer2(vqInst).start();
}
}
class VariableQueue {
private int value;
/**
* @return the value
*/
public synchronized int getValue() {
return value;
}
/**
* @param value the value to set
*/
public synchronized void setValue(int value) {
this.value = value;
}
}
class Producer extends Thread {
private VariableQueue vqInst;
public Producer(VariableQueue vqInst) {
this.vqInst = vqInst;
}
public void run() {
while (true) {
synchronized (vqInst) {
vqInst.setValue(vqInst.getValue() + 1);
vqInst.notifyAll();
try {
vqInst.wait(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Consumer1 extends Thread {
private VariableQueue vqInst;
public Consumer1(VariableQueue vqInst) {
this.vqInst = vqInst;
}
public void run() {
while (true) {
synchronized (vqInst) {
while (vqInst.getValue() % 2 == 0) {
System.out.println("模2余0==>".concat(String.valueOf(vqInst.getValue())));
try {
vqInst.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Consumer2 extends Thread {
private VariableQueue vqInst;
public Consumer2(VariableQueue vqInst) {
this.vqInst = vqInst;
}
public void run() {
while (true) {
synchronized (vqInst) {
while (vqInst.getValue() % 2 == 1) {
System.out.println("模2余1==>".concat(String.valueOf(vqInst.getValue())));
try {
vqInst.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
2.条件变量
package org.ybygjy.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 等待-通知
* @author WangYanCheng
* @version 2012-11-18
*/
public class ConditionVariable {
private VariableQueue vqInst;
public ConditionVariable() {
vqInst = new VariableQueue();
}
// TODO 1.一个线程负责生产
// TODO 2.二个线程负责消费
// TODO 2.1一个线程负责消费%2==0
// TODO 2.2一个线程负责消费%2==1
public static void main(String[] args) {
ConditionVariable cvInst = new ConditionVariable();
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(cvInst.new Producer(lock, condition)).start();
new Thread(cvInst.new Consumer1(lock, condition)).start();
new Thread(cvInst.new Consumer2(lock, condition)).start();
}
class VariableQueue {
private int value;
/**
* @return the value
*/
public int getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(int value) {
this.value = value;
}
}
class Producer extends Thread {
private Lock lock;
private Condition condition;
public Producer(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
public void run() {
try {
lock.lock();
while (true) {
vqInst.setValue(vqInst.getValue() + 1);
condition.signalAll();
condition.await();
// condition.await(500, TimeUnit.MILLISECONDS);
sleep(500);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Consumer1 extends Thread {
private Lock lock;
private Condition condition;
public Consumer1(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
public void run() {
try {
lock.lock();
while (true) {
if (vqInst.getValue() % 2 == 0) {
System.out.println("模2余0==>".concat(String.valueOf(vqInst.getValue())));
condition.signal();
}
condition.await();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Consumer2 extends Thread {
private Lock lock;
private Condition condition;
public Consumer2(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
public void run() {
try {
lock.lock();
while (true) {
if (vqInst.getValue() % 2 == 1) {
System.out.println("模2余1==>".concat(String.valueOf(vqInst.getValue())));
condition.signal();
}
condition.await();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
后续
当前示例代码中关于条件变量相关并未实际应用,所以并不一定完全正确,还有待检验。
资料