1 传统通信方式
使用Object类的wait()、notify()、notifyAll()方法实现。
这三个方法的调用:
- 这三个方法不属于Thread类,属于Object类。
- 这三个方法必须由同步监视器调用。(所以(1)同步方法中同步监视器就是this;同步代码块中同步监视器就是参数中的实例。(2)传统通信方式只适用于,使用同步监视器(synchronized)方实现线程同步的情况。)
注意:传统通信方式只适用于,使用同步监视器(synchronized)方实现线程同步的情况。
三个方法的解释:
- wait():令当前线程等待。
释放锁:当前线程调用wait() 方法时,会释放对该同步监视器的锁定。 - notify():唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,会选择唤醒其中的一个线程,选择是任意的。
释放锁:只有当前线程释放对该同步监视器的锁定后,才可以执行被唤醒的线程。 - notifyAll():唤醒在此同步监视器上等待的所有线程。
释放锁:只有当前线程释放对该同步监视器的锁定后,才可以执行被唤醒的线程。
例子:取钱与存钱
(1)账户:有钱,没钱。
(2)取钱者来取钱,若账户里没钱,就等待wait()。若账户里有钱,就取光,然后通知notifyAll()存钱者存钱。
(3)存钱者来存钱,若账户里有钱,就等待wait()。若账户里没钱,就存钱,然后通知notifyAll()取钱者来取钱。
package com.comunication;
public class Test {
boolean flag = false;// flag为假,表示没钱,真,表示有钱。
//同步方法
private synchronized void draw() {// 取钱方法
try {
if (flag) {// 有钱,取钱,然后通知存钱者存钱
flag = false;
this.notifyAll();// 唤醒存钱线程
} else {// 没钱,等着
this.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//同步方法
private synchronized void deposit() {
try {
if (flag) {// 有钱,不存,等待
wait();
} else {// 没钱,存钱,通知取钱者来取钱
flag = true;
notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
//同步代码块的测试
private Account account;
private void testBlock() {
//同步代码块
synchronized (account) {
try {
account.wait();
account.notify();
account.notifyAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Account{
}
2 使用condition控制线程通信
如果系统不使用同步监视器(synchronized)实现同步,而直接使用Lock对象实现同步。那么就不能使用wait()、notify()、 notifyAll()方法进行线程通信了。(只有同步监视器可以调用者三个方法)
使用Lock对象保证同步时,使用Condition对象保持协调。(await()是当前线程等待,signal()唤醒某个线程,signalAll()唤醒所有线程)
三个方法的解释:
- await():使当前线程等待。
- signal():唤醒在该Lock对象上等待的单个线程。如果所有线程都在该Lock对象上等待,则选择其中一个唤醒,选择是任意的。
只有当前线程放弃对该Lock对象的锁定后,才可以执行被唤醒的线程。 - signalAll():唤醒在该Lock对象上等待的所有线程。
只有当前线程放弃对该Lock对象的锁定后,才可以执行被唤醒的线程。
package com.comunication;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionSample {
boolean flag = false;//没钱
private final Lock lock = new ReentrantLock();
private final Condition cond = lock.newCondition();
private void draw() {//取钱
lock.lock();
try {
if(flag) {//有钱,取钱,通知存钱者来存钱
flag = true;//取钱
cond.signalAll();
}else {//没钱
cond.await();//等待
}
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
private void deposit() {
lock.lock();
try {
if(flag) {//有钱,等着
cond.await();
}else {//没钱,存钱,通知取钱者来取钱
flag = true;
cond.signalAll();
}
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
}
3 BlockingQueue
生产者线程向队列中放入(put())元素,如果队列已满,则该线程被阻塞。
消费者线程从队列中取(take())元素,如果队列为空,则该线程被阻塞。