java多线程之wait、notify、notifyAll

红太狼 2022-05-14 11:22 377阅读 0赞

在java中,线程间通信可以使用wait、notify、notifyAll来进行控制。注意:这3个方法是Object的方法。

在调用一个对象的wait、notify、notifyAll方法时,必须持有该对象的锁。否则会报下面的错误:

  1. Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
  2. at java.lang.Object.notify(Native Method)
  3. at com.tommy.core.test.threadtest.SubThread.run(Test1.java:58)

多个线程都持有同一个对象的时候,如果都要进入synchronized(obj){……}的内部,就必须拿到这个对象的锁,synchronized的机制保证了同一时间最多只能有1个线程拿到了对象的锁.

wait:线程自动释放其占有的对象锁,并等待notify
notify:唤醒一个正在wait当前对象锁的线程,并让它拿到对象锁
notifyAll:唤醒所有正在wait前对象锁的线程

notify和notifyAll的最主要的区别是:notify只是唤醒一个正在wait当前对象锁的线程,而notifyAll唤醒所有。值得注意的是:notify是本地方法,具体唤醒哪一个线程由虚拟机控制;notifyAll后并不是所有的线程都能马上往下执行,它们只是跳出了wait状态,接下来它们还会是竞争对象锁。

永远在循环(loop)里调用 wait 和 notify,不是在 If 语句。
原因参考:如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例

下面通过一个IBM的多线程面试题来说明。
实现的功能是:主线程和子线程一次执行1次,共执行10次。

共享资源对象

  1. class R {
  2. // true:主线程运行标志;false:子线程运行标志。
  3. boolean masterRunFlag = true;
  4. }

主线程:

  1. class MasterThread extends Thread {
  2. private R r;
  3. public MasterThread(R r) {
  4. this.r = r;
  5. }
  6. @Override
  7. public void run() {
  8. int i = 0;
  9. while (true) {
  10. synchronized (r) {
  11. if (i == 10) {
  12. break;
  13. }
  14. while (!r.masterRunFlag) {
  15. try {
  16. System.out.println("主线程等待执行.flag="+r.masterRunFlag);
  17. r.wait();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. r.masterRunFlag = false;
  23. System.out.println("主线程第" + (i + 1) + "次执行。flag="+r.masterRunFlag);
  24. i++;
  25. r.notify();
  26. }
  27. }
  28. System.out.println("主线程执行完毕。。。。");
  29. }
  30. }

子线程:

  1. class SubThread extends Thread {
  2. private R r;
  3. public SubThread(R r) {
  4. this.r = r;
  5. }
  6. @Override
  7. public void run() {
  8. int i = 0;
  9. while (true) {
  10. if (i == 10) {
  11. break;
  12. }
  13. synchronized (r) {
  14. while (r.masterRunFlag) {
  15. try {
  16. System.out.println("子线程等待执行。flag="+r.masterRunFlag);
  17. r.wait();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. r.masterRunFlag = true;
  23. System.out.println("子线程第" + (i + 1) + "次执行完毕。flag="+r.masterRunFlag);
  24. i++;
  25. r.notify();
  26. }
  27. }
  28. System.out.println("子线程执行完毕。。。。");
  29. }
  30. }

测试类:

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. R r = new R();
  4. MasterThread mt = new MasterThread(r);
  5. SubThread st = new SubThread(r);
  6. mt.start();
  7. st.start();
  8. }
  9. }

执行结果:

  1. 主线程第1次执行。flag=false
  2. 主线程等待执行.flag=false
  3. 子线程第1次执行完毕。flag=true
  4. 子线程等待执行。flag=true
  5. 主线程第2次执行。flag=false
  6. 主线程等待执行.flag=false
  7. 子线程第2次执行完毕。flag=true
  8. 子线程等待执行。flag=true
  9. 主线程第3次执行。flag=false
  10. 主线程等待执行.flag=false
  11. 子线程第3次执行完毕。flag=true
  12. 子线程等待执行。flag=true
  13. 主线程第4次执行。flag=false
  14. 主线程等待执行.flag=false
  15. 子线程第4次执行完毕。flag=true
  16. 子线程等待执行。flag=true
  17. 主线程第5次执行。flag=false
  18. 主线程等待执行.flag=false
  19. 子线程第5次执行完毕。flag=true
  20. 子线程等待执行。flag=true
  21. 主线程第6次执行。flag=false
  22. 主线程等待执行.flag=false
  23. 子线程第6次执行完毕。flag=true
  24. 子线程等待执行。flag=true
  25. 主线程第7次执行。flag=false
  26. 主线程等待执行.flag=false
  27. 子线程第7次执行完毕。flag=true
  28. 子线程等待执行。flag=true
  29. 主线程第8次执行。flag=false
  30. 主线程等待执行.flag=false
  31. 子线程第8次执行完毕。flag=true
  32. 子线程等待执行。flag=true
  33. 主线程第9次执行。flag=false
  34. 主线程等待执行.flag=false
  35. 子线程第9次执行完毕。flag=true
  36. 子线程等待执行。flag=true
  37. 主线程第10次执行。flag=false
  38. 主线程执行完毕。。。。
  39. 子线程第10次执行完毕。flag=true
  40. 子线程执行完毕。。。。

注意:主线程(生产者)和子线程(消费者)的执行次数必须相同。因为主线程和子线程的执行都依赖对方改变masterRunFlag的状态。
所以这里主线程执行10次,子线程也必须执行10次。

生产者、消费者模型,可以参考:https://www.jianshu.com/p/f7d4819b7b24

发表评论

表情:
评论列表 (有 0 条评论,377人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java线线

    java多线程之线程池 池化技术 程序的运行,其本质上,是对系统资源(CPU、内存、磁盘、网络等等)的使用。如何高效的使用这些资源是我们编程优化演进的一个方向。今天说

    相关 Java线线同步

    线程安全问题 在单线程中不会出现线程安全问题,而多线程编程中,如果多个线程同时操作同一个资源,这种资源可以是各种类型的的资源:一个变量、一个对象、一个文件、一个数据库表等

    相关 Java线守护线

    守护线程在没有用户线程可服务时自动离开,在Java中比较特殊的线程是被称为守护(Daemon)线程的低级别线程。这个线程具有最低的优先级,用于为系统中的其它对象和线程提供服务。

    相关 java线

    线程的概念 线程:是进程中的一个单一的连续控制流程。一个进程可以拥有多个线程。 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程

    相关 java线

    【线程】 线程是程序里面不同的执行路径。 Java的线程是通过java.lang.Thread类来实现的。 VM启动时会有一个由主方法(public static voi