本篇博文介绍的是多线程中线程间通信入门的实例。
- 使用线程P表示消费数据线程,使用线程C表示生产数据线程。如果程序开始时,线程C抢到了CPU执行权,发现isProduce为false即没有数据供其消费,便调用
Lock.wait()
,使得C线程进入了锁对象LOCK
的线程等待池中(注意:wait()
方法是会释放锁对象的);此时P线程抢到了CPU执行权,发现isProduce为false,就会去生产数据,生产完数据后,调用Lock.notify()
,唤醒在锁对象LOCK
线程等待池中的线程(这里是C线程),使得C线程进入LOCK锁的锁池中,并且将isProduce置为true,表示已经生产了数据;下一步C线程抢到了CPU执行权,发现isProduce为true,便会消费数据,并调用LOCK.notify()
唤醒P线程重新生产数据,并将isProduce置为false,表示没有数据了。
就这样,一直循环下去,生产数据线程与消费数据线程一直交替运行,不会出现任何错误!
注:如果对博文中提及的线程等待池、锁池及sleep()
、wait()
不熟悉的读者,可以参考下面这篇文章:点击一下
同时,如果是多个Producer和多个Consumer之间的通信,使用上面的代码会发生假死锁的现象,有机会我也会写一篇博客介绍这种情况。有兴趣的读者,可以关注博主!!!
下面是代码部分:
```java
package exercise01.src.com.yyh.day08;
public class ProduceConsumeVersion2 {
private final Object LOCK = new Object();
private int i = 0;
private volatile boolean isProduce = false; //false表示没有生产,true表示已经生产了
//生产数据
private void produce(){
synchronized (LOCK){
if(isProduce){ //isProduce为true,表示已经生产过了,就进入等待状态,
try {
//调用wait方法后,拥有该锁的生产数据线程进入等待池中,而等待池中的线程不会去竞争锁的;
// 只有【锁对象】调用了notify或notifyAll方法,才使得该锁对象上的等待池中的线程进入锁池中,
// 然后去竞争锁对象
LOCK.wait();//wait()是会释放锁对象的
} catch (InterruptedException e) {
e.printStackTrace();
}
}else { //如果没有生产的话,就要生产数据了,然后通知消费线程来消费数据
i++;
System.out.println("P--> "+i); //生产数据
LOCK.notify(); //唤醒消费数据的线程
isProduce = true;//表示已经生产,好让消费线程进行判断
}
}
}
//消费数据
private void consume(){
synchronized (LOCK){
if(!isProduce){ //如果没有生产,消费数据线程就会进入LOCK锁对象的线程等待池中
try {
LOCK.wait();//wait()是会释放锁对象的
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println("C--> "+i); //消费数据
LOCK.notify(); //消费完了以后,就要唤醒在锁对象LOCK的等待池线程,即生产数据线程
isProduce = false; //消费完以后,唤醒生产数据线程,同时将标志置为false,表示没生产
}
}
}
public static void main(String[] args) {
ProduceConsumeVersion2 pc2 = new ProduceConsumeVersion2();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
pc2.consume();
}
}
},"C线程").start();
new Thread("P线程"){
@Override
public void run() {
while (true) {
pc2.produce();
}
}
}.start();
}
}