等待与通知
在java中,Object类有方法Object.wait()/Object.wait(long)和Object.notify()/Object.notifyAll()可以实现等待和通知;
- Object.wait() 线程暂停,等待唤醒
- Object.wait(long) 线程暂停一段时间,等待唤醒,如果超时自动启动
- Object.notify()唤醒被暂停的任意一个线程
- Object.notifyAll()唤醒被暂停的所有线程
注意:Object.wait()方法和Thread.sleep()方法有点相似。
Object.wait():释放cpu资源,等待唤醒;
Thread.sleep():占用cpu资源,休眠结束之后继续执行;
实例代码:
高仿生成消费的小测试:list的值等待消费,生产者每隔一秒钟生产一个;
// 测试当count=10 的时候对count 赋值0
private static List<String> list = new LinkedList<>();
public static void main(String[] args) {
new Thread(()->{
while (true){
System.out.println("第一个线程获取锁ing");
synchronized (list){
while (list.isEmpty()){
System.out.println("第一个线程消费完成,等待生产者生产");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.remove(0);
}
}
}).start();
// 一秒钟生成一个
for(int i=0;i<20;i++){
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (list){
System.out.println("生产一个");
list.add(i+"");
list.notify();
}
}
}
说明:wait方法和notify方法一定要synchronized包含,否则会报如下错误, 等待线程和通知线程必须调用同一个对象的wait方法和notify方法;
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at com.WaitTest.main(WaitTest.java:42)
内部实现原理
java虚拟机会为每个对象维护一个入口集(Entry Set)用于存储申请该对象内部锁的线程。另外,java
虚拟机还会为每个对象维护一个被等待集(Wait Set)的队列,该队列用户存储该对象的等待集合。
Object.wait()将当前线程暂停并释放相应的内部锁的同时会将当前线程的引用存入该方法所属对象的等待集合中
(wait方法会一直卡住,没有返回)。执行一个对象的notify方法会使该对象的任意一个线程被唤醒。被唤醒的线
程需要等待Object.wait()吧当前线程移除等待集合,接着Object.wait()方法返回。Object.wait()/notify()等待通知机
制的几个关键步骤:当前线程加入等待集合、暂停当前线程、释放锁以及将唤醒后的线程从等待线程移除,都
是在Object.wait()方法中实现的。
Thread.join()说明
Thread.join/Thread.join(long millis)可以使当前线程等待目标线程结束之后再继续执行。
join(long)是一个同步方法。它检测到目标线程没有结束会调用wait方法暂停当前线程,直到目标线程已经终止。
如下,如果不使用join则会很快的打印出来“主线程执行完毕”,但是使用了join方法会先打印“子线程执行完毕”再打印“主线程执行完毕”
public static void main(String[] args) {
Thread thread = new Thread(()->{
try {
Thread.sleep(5000l);
System.out.println("子线程执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行完毕");
}
2.java条件变量
Object.wait()/notify()无法解决,过早唤醒的问题,还有wait(long)方法无法区分等待超时还是被通知线程唤醒的问题;
java条件变量即可解决以上问题。
java.util.concurrent.locks.Condition接口
public interface Condition {
//与Object.wait()用法一致
void await() throws InterruptedException;
//当前线程在接到信号之前一直处于等待状态。 注:该方法不响应中断
void awaitUninterruptibly();
//与Object.wait()方法一致
boolean await(long time, TimeUnit unit) throws InterruptedException;
//deadline 是等待的时刻
//false表示等待超时
boolean awaitUntil(Date deadline) throws InterruptedException;
//和awaitUntil类似,区别是等待时间,单位为纳秒
// 1秒==1000毫秒==1000000微妙==1000000000纳秒
long awaitNanos(long nanosTimeout) throws InterruptedException;
//与Object.wait()方法效果一样
void signal();
void signalAll();
}
Object.wait()/notify()是通过synchronized
配合使用,Condition
接口是在java.util.concurrent.locks.ReentrantLock
下使用。