线程通信-等待通知机制
线程通信-等待通知机制
非等待的线程间通信
public void run() { try { for (int i = 0; i < 10; i++) { list.add(); System.out.println(“添加了” + (i + 1) + “个元素”); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } public void run() { try { while (true) { if (list.size() == 5) { System.out.println(“==5了,线程b要退出了!”); throw new InterruptedException(); } } } catch (InterruptedException e) { e.printStackTrace(); } }
这样写有什么问题呢?
- 两个线程实现了通信,但list大小为5的时候, 线程B退出了, 但是线程B不停的轮询是否为5, 这个时候是很占资源的
- 如果轮询的时间间隔小, 这个时候更加浪费资源
- 如果轮询的时间间隔大, 那么还可能错过想要的数据, 数据的实时性也会变得比较差
- 这里共享了list, 所以实现了通信, 但是因为不知道什么时候通信, 所以不停的轮询, 这种通信是有缺点的:
- 一是浪费了cpu资源 (因为始终占据着cpu进行轮询)
- 二是可能读取到错误的数据 (高并发时, 可能会读到错误的数据)
2.java中的wait/notify机制
线程A要等待线程B发出通知才执行, 这个时候线程A可以执行wait()方法, 等待线程B执行notify() 方法唤醒线程A
wait/notify的简单实现
public void run() { try { synchronized (lock) { if (MyList.size() != 5) { System.out.println(“wait begin” + System.currentTimeMillis()); lock.wait(); System.out.println(“wait end” + System.currentTimeMillis()); } } } catch (InterruptedException e) { e.printStackTrace(); } } public void run() { try { synchronized (lock) { for (int i = 0; i < 10; i++) { MyList.add(); if (MyList.size() == 5) { lock.notify(); System.out.println(“已发出通知!”); } System.out.println(“添加了” + (i + 1) + “个元素!”); Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } }
将上面的代码进行更改,当大小不等于5的时候,线程A处于wait状态,直到线程B发出通知,唤醒线程A,通过等待通知机制,避免了线程A不停轮询造成的资源浪费
wait/notify机制注意事项
- wait()和notify()必须是在同步方法和同步代码块里面调用,要不然会抛出异常
- notify()方法是继承自Object类,可以唤醒在此对象监视器等待的线程,也就是说唤醒的是同一个锁的线程
- notify()方法调用之后,不会马上释放锁,而是运行完该同步方法或者是运行完该同步代码块的代码
- 调用notify()后随机唤醒的是一个线程
- 调用wait()方法后会将锁释放
- wait()状态下中断线程会抛出异常
- wait(long),超过设置的时间后会自动唤醒,还没超过该时间也可以通过其他线程唤醒
- notifyAll()可以唤醒同一锁的所有线程
- 如果线程还没有处于等待状态,其他线程进行唤醒,那么不会起作用,此时会打乱程序的正常逻辑