一、wait、notify、notifyAll
1.1 方法简介
1)调用某个对象的
wait()
方法,相当于让当前线程交出此对象的锁,然后进入等待状态,等待后续再次获得此对象的锁
(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁)。如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
2)notify()
方法能够唤醒一个正在等待该对象的锁的线程,当有多个线程都在等待该对象的锁的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。调用某个对象的notify()方法,当前线程也必须拥有这个对象的锁,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
3)notifyAll()
方法能够唤醒全部正在等待该对象的锁的线程,最终谁能拿到锁不得而知。用某个对象的notifyAll()方法,当前线程也必须拥有这个对象的锁,因此调用notifyAll()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
注意:
一个线程被唤醒不代表立即获取了对象的锁,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
1.2 相关方法
方法名称 | 描述 |
---|---|
wait() | 调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用wait()方法后,会释放对象的锁。 |
wait(long) | 超时等待一段时间,这里的参数是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。 |
wait(long, int) | 对于超时时间更细粒度的控制,可以达到毫秒。 |
notify() | 通知一个在对象上等待的线程,使其从wait()返回,而返回的前提是该线程获取到了对象的锁。 |
notifyAll() | 通知所有等待在该对象上的线程。 |
1.3 示例
public class Test {
Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Runnable runnable1 = new Runnable() {
@Override
public void run() {
test.await(); // 等待被唤醒
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
test.anotify(); // 唤醒工作线程
}
};
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
thread1.start();
thread2.start();
}
public void await() {
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + ": 等待被唤醒");
lock.wait();
System.out.println("开始工作...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void anotify() {
synchronized (lock) {
try {
System.out.println("10秒后唤醒工作线程...");
TimeUnit.SECONDS.sleep(10);
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
二、Condition
2.1 简介
任何一个java对象都天然继承于Object类,在线程间实现通信的往往会应用到Object的几个方法,比如wait(),wait(long timeout),wait(long timeout, int nanos)与notify(),notifyAll()几个方法实现等待/通知机制,同样的, 在java Lock体系下依然会有同样的方法实现等待/通知机制。从整体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是java底层级别的,后者是语言级别的,具有更高的可控制性和扩展性。两者除了在使用方式上不同外,在功能特性上还是有很多的不同:
- Condition能够支持不响应中断,而通过使用Object方式不支持;
- Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;
- Condition能够支持超时时间的设置,而Object不支持
2.2 先关方法
方法 | 描述 |
---|---|
await() | 造成当前线程在接到信号或被中断之前一直处于等待状态。 |
await(long time, TimeUnit unit) | 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 |
awaitNanos(long nanosTimeout) | 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。 |
awaitUninterruptibly() | 造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。 |
awaitUntil(Date deadline) | 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。 |
signal() | 唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。 |
signalAll() | 唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。 |
2.3 示例
public class Test {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Runnable runnable1 = new Runnable() {
@Override
public void run() {
test.await(); // 等待被唤醒
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
test.anotify(); // 唤醒工作线程
}
};
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
thread1.start();
thread2.start();
}
public void await() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + ": 等待被唤醒");
condition.await();
System.out.println("开始工作...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void anotify() {
lock.lock();
try {
System.out.println("10秒后唤醒工作线程...");
TimeUnit.SECONDS.sleep(10);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
三、两者比较
对比项 | Object Monitor Methods | Condition |
---|---|---|
前置条件 | 获取对象的锁 | 调用Lock.lock()获取锁 调用Lock.newCodition()获取Condition对象 |
调用方法 | 直接调用 如:Object.wait() | 直接调用 如:condition.await() |
等待队列个数 | 一个 | 多个 |
当前线程释放锁进入等待状态 | 支持 | 支持 |
当前线程释放锁进入等待状态,在等待状态中不响应中断 | 不支持 | 支持 |
当前线程释放锁并进入超时等待状态 | 支持 | 支持 |
当前线程释放锁并进入等待状态到将来某个时间 | 不支持 | 支持 |
唤醒等待队列中的一个线程 | 支持 | 支持 |
唤醒等待队列中的全部线程 | 支持 | 支持 |