机制的由来
一 个 线 程 修 改 了 一 个 对 象 的 值, 而 另 一 个 线 程 感 知 到 了 变 化, 然 后 进 行 相 应 的 操 作, 整 个 过 程 开 始 于 一 个 线 程, 而 最 终 执 行 又 是 另 一 个 线 程。 前 者 是 生 产 者, 后 者 就 是 消 费 者, 这 种 模 式 隔 离 了“ 做 什 么”( what) 和“ 怎 么 做”( How), 在 功 能 层 面 上 实 现 了 解 耦, 体 系 结 构 上 具 备 了 良 好 的 伸 缩 性, 但 是 在 Java 语 言 中 如 何 实 现 类 似 的 功 能 呢?
运行机制
等 待/ 通 知 机 制, 是 指 一 个 线 程 A 调 用 了 对 象 O 的 wait() 方 法 进 入 等 待 状 态, 而 另 一 个 线 程 B 调 用 了 对 象 O 的 notify() 或 者 notifyAll() 方 法, 线 程 A 收 到 通 知 后 从 对 象 O 的 wait() 方 法 返 回, 进 而 执 行 后 续 操 作。 上 述 两 个 线 程 通 过 对 象 O 来 完 成 交 互, 而 对 象 上 的 wait() 和 notify/ notifyAll() 的 关 系 就 如 同 开 关 信 号 一 样, 用 来 完 成 等 待 方 和 通 知 方 之 间 的 交 互 工 作。
主要方法
public synchronized void testRun() {
System.out.println("执行 " + Thread.currentThread().getName());
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行结束");
}
public static void main(String[] args) throws InterruptedException {
MainClass4 class4 = new MainClass4();
//A线程中进入等待
new Thread(new Runnable() {
@Override
public void run() {
class4.testRun();
}
}).start();
Thread.sleep(300);
//B线程中唤醒
synchronized (class4) {
class4.notify();
}
}
打印
执行 Thread-0
Thread-0执行结束
唤醒全部
notify只会唤醒待会此对象的线程,但是notifyAll会唤醒全部等待此对象的线程。
public static void main(String[] args) throws InterruptedException {
MainClass4 class4 = new MainClass4();
for (int i = 0; i < 4; i++) {
new Thread(new Runnable() {
@Override
public void run() {
class4.testRun();
}
}).start();
}
Thread.sleep(300);
synchronized (class4) {
class4.notify();
}
System.out.println("notifyAll");
synchronized (class4){
class4.notifyAll();
}
}
输出
执行 Thread-0
执行 Thread-3
执行 Thread-2
执行 Thread-1
notifyAll
Thread-0执行结束
Thread-1执行结束
Thread-2执行结束
Thread-3执行结束
等待通知机制注意事项
-
调用必须在同步下进行
可以看到代码中调用wait()和nofity、nofityAll的地方都加入了synchronized同步关键字,说明等待通知的调动必须在同步下进行。 -
等待和通知调用必须在不同线程
代码中wait的地方是在一个子线程,notify的地方是在main线程,它们不是在一个线程的,这也满足之前说的作用于不同线程直接的通信 -
必须是同一把锁
代码中的的synchronized用的都是class4对象的锁,等待通知机制如果不是同一把锁会报监视器错误
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at MainClass4.main(MainClass4.java:20)
- 必须是同一个对象
等待的是那个对象,唤醒的就是那个对象
网友评论