什么是 Monitor?
Java 虚拟机给每个对象和 class 字节码都设置了一个监听器 Monitor,用于检测并发代码的重入,同时在 Object 类中还提供了 notify 和 wait 方法来对线程进行控制
在线程的同步起着关键的作用, 假设有如下代码
1 2 3 4 5 6 7 8 9 10 11 |
public class ThreadTest { private String str; private synchronized void println2(String str){ this.str = str; } public static synchronized void println(){ System.out.println("hello world"); } } |
在上述方法中, 当执行到非静态方法 println2 时, 需要获取当前对象 this 的 Monitor , 获取后, 其他需要获取该对象的Monitor 的线程会被堵塞。
当执行到静态方法 println 时, 需要获取到当前类的字节码的 Monitor (因为静态方法不属于对象, 而属于类) , 获取后, 其他需要 Monitor 的线程会被堵塞。
假设有如下的示例, 分别有线程 1 和线程 2 , 一个线程去赋值数据, 一个线程在得到数据后使用这个数据, (听起来有点拗口, 不如想象成一个线程去请求数据, 一个线程在请求到数据之后将数据渲染到界面上)这里线程 1 和线程 2 都模拟了一下耗时操作, 这里是写死耗时时间, 在实际的情况中, 可能是不定时的, 我们来看看打印的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class ThreadTest { private String str; public synchronized void initStr() { str = "hello world"; } public synchronized void printStr() { System.out.println(str); } public static void main(String[] args) { ThreadTest threadTest = new ThreadTest(); Thread thread = new Thread(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } threadTest.printStr(); }); thread.start(); Thread thread2 = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadTest.initStr(); }); thread2.start(); } } |
打印的结果
1
|
null |
这里打印的结果可想而知, 相信大家都可以猜到, 但是这里并不符合我们的需求, 我们需要的是一个线程请求到数据后, 另外一个线程使用这个数据, 稍加改造一下
1 2 3 4 5 6 |
public synchronized void printStr() { while (str == null) { } System.out.println(str); } |
我们在这里加一个判断, 当 str 为 null 的时候, 死循环, 就不打印他的值, 也就是说当另外一个线程赋值成功后, 这里将不为null, 然后就会打印值, 真的是这样吗? run 一下程序后会发现, 一直也不会打印, 这是为什么?
这是因为, 当我们执行到 printStr 的时候, 持有了当前类对象的 Monitor, 而这个时候恰好另外一个线程去赋值了, 发现这个时候当前类对象的 Monitor 被别人给持有了, 将发生堵塞, 那么将一直不会被执行。
再进行下一步改造, 如果我们在没有拿到数据之前稍微等一会, 并且不持有 Monitor , 然后另外一个赋值的时候不是刚刚好可以赋值了吗? 赋值成功后, 我们再通知线程已经有值了, 再打印一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public synchronized void initStr() { str = "hello world"; notifyAll(); } public synchronized void printStr() { while (str == null) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(str); |
打印结果
1
|
hello world |
wait: 当前线程会进入等待, 释放当前的 Monitor
notifyAll: 唤醒所有在等待的线程