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

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

1
2
3
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	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
2
3
4
class R {
    // true:主线程运行标志;false:子线程运行标志。
    boolean masterRunFlag = true;
}

主线程:

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
class MasterThread extends Thread {
    private R r;
    public MasterThread(R r) {
        this.r = r;
    }
    @Override
    public void run() {
        int i = 0;
        while (true) {
            synchronized (r) {
                if (i == 10) {
                    break;
                }
                while (!r.masterRunFlag) {
                    try {
                        System.out.println("主线程等待执行.flag="+r.masterRunFlag);
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                r.masterRunFlag = false;
                System.out.println("主线程第" + (i + 1) + "次执行。flag="+r.masterRunFlag);
                i++;
                r.notify();
            }
        }
        System.out.println("主线程执行完毕。。。。");
    }
}

子线程:

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
class SubThread extends Thread {
    private R r;
    public SubThread(R r) {
        this.r = r;
    }
    @Override
    public void run() {
        int i = 0;
        while (true) {
            if (i == 10) {
                break;
            }
            synchronized (r) {
                while (r.masterRunFlag) {
                    try {
                        System.out.println("子线程等待执行。flag="+r.masterRunFlag);
                        r.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                r.masterRunFlag = true;
                System.out.println("子线程第" + (i + 1) + "次执行完毕。flag="+r.masterRunFlag);
                i++;
                r.notify();
            }
        }
        System.out.println("子线程执行完毕。。。。");
    }
}

测试类:

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

执行结果:

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
37
38
39
40
主线程第1次执行。flag=false
主线程等待执行.flag=false
子线程第1次执行完毕。flag=true
子线程等待执行。flag=true
主线程第2次执行。flag=false
主线程等待执行.flag=false
子线程第2次执行完毕。flag=true
子线程等待执行。flag=true
主线程第3次执行。flag=false
主线程等待执行.flag=false
子线程第3次执行完毕。flag=true
子线程等待执行。flag=true
主线程第4次执行。flag=false
主线程等待执行.flag=false
子线程第4次执行完毕。flag=true
子线程等待执行。flag=true
主线程第5次执行。flag=false
主线程等待执行.flag=false
子线程第5次执行完毕。flag=true
子线程等待执行。flag=true
主线程第6次执行。flag=false
主线程等待执行.flag=false
子线程第6次执行完毕。flag=true
子线程等待执行。flag=true
主线程第7次执行。flag=false
主线程等待执行.flag=false
子线程第7次执行完毕。flag=true
子线程等待执行。flag=true
主线程第8次执行。flag=false
主线程等待执行.flag=false
子线程第8次执行完毕。flag=true
子线程等待执行。flag=true
主线程第9次执行。flag=false
主线程等待执行.flag=false
子线程第9次执行完毕。flag=true
子线程等待执行。flag=true
主线程第10次执行。flag=false
主线程执行完毕。。。。
子线程第10次执行完毕。flag=true
子线程执行完毕。。。。

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

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