JAVA多线程基础篇 7、线程协作 wait()和notify()

文章目录

  • 1 用wait和notify实现线程同步
    • 1.1 实现方法
  • 2 也可以使用ReentrantLock和condition实现
  • 总结

wait()和notify()都是定义在Object类中,那为什么如此设计呢?。因为synchronized中的这把锁可以是任意对象,所以任意对象都可以调用wait()和notify(),并且只有同一把锁才能对线程进行操作,不同锁之间是不可以相互操作的。

  • wait

wait()提供三种构造方法,wait()是让线程一直处于等待状态,直到手动唤醒;而wait(long timeout)可以指定等待时间,之后会自动唤醒。

  • notify

notify()唤醒等待的线程,如果监视器种只有一个等待线程,使用notify()可以唤醒。但是如果有多条线程notify()是随机唤醒其中一条线程,与之对应的就是notifyAll()就是唤醒所有等待的线程。

1 用wait和notify实现线程同步

这是leetcode的1115题。

class FooBar {
  public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
    }
  }

  public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
  }
}

两个不同的线程将会共用一个 FooBar 实例:

线程 A 将会调用 foo() 方法,而
线程 B 将会调用 bar() 方法
请设计修改程序,以确保 “foobar” 被输出 n 次。

示例 1:

输入:n = 1
输出:“foobar”
解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,“foobar” 将被输出一次。
示例 2:

输入:n = 2
输出:“foobarfoobar”
解释:“foobar” 将被输出两次。

提示:

1 <= n <= 1000

1.1 实现方法

增加flag状态。在synchronized的时候,foo方法和bar方法监控flag的状态。

  • Foo:
    • 如果flag是Flase,则会wait。
    • 如果自己被唤醒,则表示,Bar完成了其任务,该Foo继续完成任务。
    • 于是Foo打印
    • 打印完成了后将标志flag重置为相反。
  • Bar:
    • 如果flag是true,则会wait。
    • 如果自己被唤醒,则表示,Bar完成了其任务,该Foo继续完成任务。
    • 于是Foo打印
    • 打印完成了后将标志flag重置为相反。
class FooBar {

        private int n;

        private volatile boolean flag = true;

        public FooBar(int n) {
            this.n = n;
          }

        public void foo(Runnable printFoo) throws InterruptedException {

            for (int i = 0; i < n; i++) {
                // printFoo.run() outputs "foo". Do not change or remove this line.
                synchronized (this){
                    while (!flag){
                        wait();
                    }
                    printFoo.run();
                    flag=!flag;
                    notify();
                }
            }
        }

        public void bar(Runnable printBar) throws InterruptedException {

            for (int i = 0; i < n; i++) {
                synchronized (this) {
                    while (flag) {
                        wait();
                    }
                    // printBar.run() outputs "bar". Do not change or remove this line.
                    printBar.run();
                    flag=!flag;
                    notify();
                }
            }
        }
    }
  • wait和while
    • 在这个程序当中wait和while是配对使用的。因为如果线程wait后,醒来第一件事就是执行wait之后的代码。
    • 如果是if 则不会进行再次判断。
    • 如果是while,则会再次判断flag标志,如果当前flag标志不相符,自己还会再次wait。

2 也可以使用ReentrantLock和condition实现

condition的signal、signalAll和await方法,可以替代notify、notifyAll、wait方法。

一个lock可以设定多个condition。每个condition后可以跟随多个等待队列。

class FooBar {

        private int n;

        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        private boolean flag = true;

        public FooBar(int n) {
            this.n = n;
          }

        public void foo(Runnable printFoo) throws InterruptedException {

            for (int i = 0; i < n; i++) {
                lock.lock();
                while (!flag){
                    condition.await();
                }
                // printFoo.run() outputs "foo". Do not change or remove this line.
                printFoo.run();
                flag=!flag;
                condition.signalAll();
                lock.unlock();
            }
        }

        public void bar(Runnable printBar) throws InterruptedException {

            for (int i = 0; i < n; i++) {
                lock.lock();
                while (flag){
                    condition.await();
                }
                // printBar.run() outputs "bar". Do not change or remove this line.
                printBar.run();
                flag=!flag;
                condition.signalAll();
                lock.unlock();
            }
        }
    }

总结

wait和nofity是成对使用的,最好使用notifyAll(),这样可以唤起这条队列里所有的线程。

同时还需要注意的是,wait和while需要配对使用。

多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。

https://github.com/forestnlp/concurrentlab

如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。

您的支持是对我最大的鼓励。

你可能感兴趣的:(JAVA多线程基础篇,java,开发语言,后端,前端,linux)