wait和notify的使用
规则
- wait和notify都只能在synchronized代码块里面调用
- synchronized(object)的语义是只有持有object锁的对象才能进入,同一时间只能有一个线程持有该锁
- wait和notify的调用者最好是final修饰的对象,否则容易出现对象被改变之后的IllegalMonitorStateException错误
- 进入了synchronized代码块代表获取了该对象的锁
- 调用object.wait代表暂时释放object锁并处于阻塞状态,这时,另外的线程就可以进入synchronized代码块了
- 调用object.notify会唤醒之前调用了object.wait的(也就是处于阻塞状态的)线程,但是锁的释放要等到当前的同步代码块执行完毕才释放
- synchronized代码块里,object.notify之后的代码会继续执行,并且能够使用object对象
使用
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author mingC
* @date 2018/7/7
*/
public class WaitAndNotify {
final Object lock = new Object();
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
class Consumer implements Runnable {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "进入同步块");
System.out.println(Thread.currentThread().getName() + "wait()");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "唤醒..");
}
}
}
class Producer implements Runnable {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "进入同步块");
System.out.println(Thread.currentThread().getName() + "notify()");
lock.notify();
System.out.println(lock);
System.out.println("测试notify调用后,后面的代码是否还执行");
}
}
}
void start() {
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Consumer());
executor.execute(new Producer());
}
public static void main(String[] args) {
new WaitAndNotify().start();
}
}
复制代码
输出
pool-1-thread-1进入同步块
pool-1-thread-1wait()
pool-1-thread-2进入同步块
pool-1-thread-2notify()
java.lang.Object@4608042a
测试notify调用后,后面的代码是否还执行
pool-1-thread-1唤醒..
复制代码
生产者消费者
合理使用wait和notify可以避免浪费cpu资源
比如生产者消费者问题,当产品数目为0时,消费者线程应当进入等待状态,避免用while(true)不断地访问,否则会浪费cpu资源。
示例 这里只考虑了单个生产者和单个消费者。
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author mingC
* @date 2018/7/7
*/
public class WaitAndNotify {
final Object readLock = new Object(); //读锁
CopyOnWriteArrayList<String> productList = new CopyOnWriteArrayList<>(); //产品列表
class Consumer implements Runnable {
@Override
public void run() {
synchronized (readLock) {
while (true) {
//如果当前产品数为0,则不能读,进入阻塞状态
if (productList.size() == 0) {
try {
System.out.println("产品数为0,消费者阻塞..");
readLock.wait();
System.out.println("消费者唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//读,这里可以保证产品数不为0
String oldValue = productList.remove(0);
System.out.println("消费产品:" + oldValue);
}
}
}
}
class Producer implements Runnable {
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒生产一件产品,模拟读取速度远大于生产速度
productList.add("产品" + count);
//唤醒消费者
if (productList.size() == 1) {
synchronized (readLock) {
readLock.notify();
}
}
count++;
}
}
}
void start() {
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Consumer());
executor.execute(new Producer());
}
public static void main(String[] args) {
new WaitAndNotify().start();
}
}
复制代码
输出:
产品数为0,消费者阻塞..
消费者唤醒
消费产品:产品0
产品数为0,消费者阻塞..
消费者唤醒
消费产品:产品1
产品数为0,消费者阻塞..
复制代码