多线程——4线程间的通信
一、内容安排
- wait和notify方法
- 简单的生产者消费者
- 面试点
二、文章内容
1. wait和notify方法
当一个对象调用了wait方法那么它将阻塞当前线程, 直到有另外一个线程调用了当前对象的notify方法,被阻塞的线程才可能被唤醒。
注意: 若要调用这两个方法必须先获得对象的锁,不让会抛出异常
- 案列: 启动A、B两个线程,A线程中调用wait方法在wait之后打印”被唤醒“; 在B线程中先打印”我来唤醒“,然后调用notify方法
public static void main(String[] args) {
//定义公用对象
Object object = new Object();
//线程A
new Thread(()->{
synchronized (object) {
try {
//调用object的wait方法阻塞当前线程
object.wait();
//打印
System.out.println("我被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//线程B
new Thread(()->{
System.out.println("我要唤醒A线程");
synchronized (object) {
try {
TimeUnit.SECONDS.sleep(3);
object.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
2.简单的生产者消费者
有了上面提到的wait和notify两个方法,我们可以利用这两个方法实现一个简单(生产者生产一个等待消费者消费再生产)的生产者消费者;
实现思路:
- 开启两个线程,一个不断的生产一个不断的消费
- 生产者线程生产消息之后唤醒等待中的消费者并让自己阻塞,等待消费者消费之后让消费者唤醒自己
- 消费者线程: 消费消息,唤醒生产者线程,然后自己阻塞
具体实现:
public class ProducerConsumer {
static Object LOCK = new Object();
static Boolean isProduced = false;
static Integer i = 0;
public static void main(String[] args) {
// 定义生产者线程
new Thread(() -> {
while (true) {
synchronized (LOCK) {
if (!isProduced) {
System.out.println("生产者生产消息" + ++i);
LOCK.notify();
isProduced = true;
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "producer").start();
// 定义消费者线程
new Thread(() -> {
while (true) {
synchronized (LOCK) {
if (isProduced) {
System.out.println("消费者生产消息" + i);
LOCK.notify();
isProduced = false;
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "consumer").start();
}
}
3.面试考点
- wait和sleep的区别
- 所属对象不同: wait是Object中的方法, sleep是Thread中的方法
- sleep不会释放锁,而wait会释放锁
- sleep可以直接使用在任何地方, 但是使用wait方法必须使用在同步带吗块儿中;并且需要使用锁对象
- sleep自动唤醒, wait需要别人唤醒