线程间通信:不同种类的线程针对同一个资源的操作。
通过设置线程(生产者)和获取线程(消费者)针对同一个学生对象进行操作。正常情况下应该是:
1、生产者:先看有没有数据,有就等待,没有就生产
2、消费者:先看有没有数据,有就消费,没有就等待
因此,java提供了一个等待唤醒机制来实现。
1、生产者:先看有没有数据,有就等待,没有就生产,生产完之后通知消费者来消费
2、消费者:先看有没有数据,有就消费,没有就等待,通知生产者该生产了
Object类提供了3个方法:
wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程
这些方法的调用必须通过锁对象调用,而我们使用的锁对象是任意锁对象,所以这些方法必须定义在Object类中。
以学生为例,分别生产和消费来演示生产者与消费者问题。(基础版)
Student类:
public class Student {
private String name;
private int age;
private boolean flag; //默认情况下没有数据,有数据就是true
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
模拟生产者类:SetThread(锁对象为this)
public class SetThread implements Runnable {
private Student s;
private int x = 0;
public SetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) { //保证锁对象是同一个锁对象
//判断有没有数据
if(s.isFlag()){ //有数据,就等待
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有数据就生产
if (x % 2 == 0) {
s.setName("小红");
s.setAge(20);
} else {
s.setName("小白");
s.setAge(10);
}
x++;
//生产完了,就有数据,改状态
s.setFlag(true); //有数据,就唤醒
s.notify();
}
}
}
}
模拟消费者类:getThread
public class GetThread implements Runnable {
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while(true){
synchronized (s) { //保证锁对象是同一个锁对象
if(!s.isFlag()){ //没有数据,就等待
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有数据,就消费
System.out.println(s.getName()+"----"+s.getAge());
//消费完了,就通知生产者该生产了
s.setFlag(false);
s.notify();
}
}
}
}
测试类:
public class ThreadDemo {
public static void main(String[] args) {
//在外部创建一个对象,然后通过构造函数传进去,确保访问的都是同一个对象
Student s = new Student();
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
t2.start();
t1.start();
}
}
优化版:
把设置和获取的操作封装成了方法,并加了同步。
设置或者获取的线程里面只需要调用方法即可。
Student类:
public class Student {
private String name;
private int age;
private boolean flag; // 默认情况下没有数据,有数据就是true
public synchronized void set(String name, int age) {
//如果有数据,就等待
if (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有就设置数据
this.name = name;
this.age = age;
//设置完数据之后就修改标记
this.flag = true;
this.notify();
}
public synchronized void get(){
//没有数据就等待
if(!this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有数据就消费
System.out.println(this.name + this.age);
//消费完了就修改数据
this.flag = false;
this.notify();
}
}
SetThread类:
public class SetThread implements Runnable {
private Student s;
private int x = 0;
public SetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
s.set("小红", 20);
} else {
s.set("小白", 10);
}
x++;
}
}
}
GetThread类:
public class GetThread implements Runnable {
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
s.get();
}
}
}
测试类:
public class ThreadDemo {
public static void main(String[] args) {
//在外部创建一个对象,然后通过构造函数传进去,确保访问的都是同一个对象
Student s = new Student();
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
t2.start();
t1.start();
}
}