1. wait()/notify()的基本使用

  • wait()/notify()只能在synchronized同步代码块中使用
synchronized(锁对象) {
	锁对象.wait();
}

synchronized(锁对象) {
	锁对象.notify();
}
  • 调用wait()方法的线程将会释放对应的锁,并被阻塞进入该锁对象的等待队列
Object lock = new Object;
new Thread(() -> {
	synchronized(lock) {
		lock.wait();
	}
}).start;
  • 调用notify()方法会随机唤醒一个该锁对象的等待队列中的线程(唤醒顺序将在后面讨论)
Object lock = new Object;
new Thread(() -> {
	synchronized(lock) {
		lock.wait();
	}
}).start;

synchronized(lock) {
	lock.notify();
}
  • 调用notifyAll()方法会唤醒该锁对象的等待队列中的所有线程
Object lock = new Object;
new Thread(() -> {synchronized(lock) {lock.wait();}}).start;
new Thread(() -> {synchronized(lock) {lock.wait();}}).start;
new Thread(() -> {synchronized(lock) {lock.wait();}}).start;

synchronized(lock) {
	lock.notifyAll();
}

2. wait()/notify()的简单应用

生产者&消费者模型

  • 需求: 生产者生产完成后通知消费者消费,消费者消费完成后,通知生产者生产
public class ThreadNotify3 {  
    public static void main(String[] args) {  
        Person person = new Person();  
        inputThread inputThread = new inputThread(person);  
        outputThread outputThread = new outputThread(person);  
        inputThread.start();  
        outputThread.start();  
    }  
}  
  
class Person {  
    public String name;  
    public char sex;  
    // 用于标识inputThread和outPutThread的行为
    public boolean flag = false;  
}  
  
class inputThread extends Thread{  
    private final Person person;  
  
    public inputThread(Person person) {  
        this.person = person;  
    }  
  
    @Override  
    public void run() {  
        int count = 0;  
        while (true) {  
            synchronized (person) {  
                if (person.flag){  
                    try {  
                        person.wait();  
                    } catch (InterruptedException e) {  
                        throw new RuntimeException(e);  
                    }  
                }  
                if (count%2 == 0) {  
                    person.name = "小明";  
                    person.sex = '男';  
                } else {  
                    person.name = "小红";  
                    person.sex = '女';  
                }  
                count++;  
                person.flag = !person.flag;  
                person.notify();  
            }  
        }  
    }  
}  
  
class outputThread extends Thread{  
    private final Person person;  
  
    public outputThread(Person person) {  
        this.person = person;  
    }  
  
    @Override  
    public void run() {  
        while (true) {  
            synchronized (person) {  
                if (!person.flag){  
                    try {  
                        person.wait();  
                    } catch (InterruptedException e) {  
                        throw new RuntimeException(e);  
                    }  
                }  
                System.out.println(person.name + "," + person.sex);  
                person.flag = !person.flag;  
                person.notify();  
            }  
        }  
    }  
}

3. notify()唤醒顺序

为了验证notify()的唤醒顺序,我编写了一下代码。

通过控制台的输出:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

这意味着,notify()是按照先后顺序依次唤醒的。但真的是这样吗?我搜索到了下面的内容:

notify()选择唤醒的线程是任意的,但具体的实现还要依赖于JVM。也就是说notify()的唤醒规则,最终取决于JVM厂商,不同的厂商的实现可能是不同的,比如阿里的JVM和Oracle的JVM,关于notify()的唤醒规则可能是不一样的。

public class ThreadNotify1 {  
  
    private static final Object lock = new Object();  
  
    public static void main(String[] args) {  
        new ThreadNotify1().print();  
    }  
  
    void print() {  
        int count = 10;  
        for (int i = 0; i < count; i++) {  
            try {  
                Thread.sleep(100);  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
            new Thread(() -> {  
                System.out.printf(Thread.currentThread().getName() + "\t");  
                synchronized (lock) {  
                    try {  
                        lock.wait();  
                    } catch (InterruptedException e) {  
                        throw new RuntimeException(e);  
                    }  
                }  
                System.out.printf(Thread.currentThread().getName() + "\t");  
            }, String.valueOf(i)).start();  
        }  
  
        for (int i = 0; i < count; i++) {  
            try{  
                Thread.sleep(100);  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
  
            synchronized (lock) {  
                lock.notify();  
            }  
        }  
    }  
}

4. join()的基本用法

join() 方法的作用是让当前线程等待调用 join() 方法的线程执行完毕后再继续执行。

public class ThreadNotify4 {  
    public static void main(String[] args) {  
        Thread thread1 = new Thread(() -> {  
            System.out.println(Thread.currentThread().getName() + "正在执行!");  
            try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
        }, "NO.1");  
  
        Thread thread2 = new Thread(() -> {  
            try {  
                thread1.join();  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
            System.out.println(Thread.currentThread().getName() + "正在执行!");  
        }, "NO.2");  
  
        Thread thread3 = new Thread(() -> {  
            try {  
                thread2.join();  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
            System.out.println(Thread.currentThread().getName() + "正在执行!");  
        }, "NO.3");  
  
        thread1.start();  
        thread2.start();  
        thread3.start();  
    }  
}