6、线程间通信(保证线程有序)
生产者-消费者问题
考虑问题如下:
1)若消费者先抢到CPU执行权,会先消费资源,但此时没有资源 ,应当生产者生产资源之后再消费;
2)若生产者先抢到CPU执行权,会先生产资源,但此时有资源,应当消费者消费资源之后再生产;
解决思路:
1)对生产者而言,先查看是否有资源,有就等待,等待消费者消费完之后,通知生产
2)对消费者而言,先查看是否有资源,无就等待,等待生产者生产完之后,通知消费
等待-唤醒机制实现线程间通信,Java提供如下API:
public final void wait()
在其他线程调用此对象的 notify()
方法或 notifyAll()
方法前,导致当前线程等待。
public final void notify()
唤醒在此对象监视器上等待的单个线程。
public final void notifyAll()
唤醒在此对象监视器上等待的所有线程。
Student类表示资源:
public class Student {
String name;
int age;
boolean flag;//标记位,表示是否有资源
}
线程间通信:
public class ThreadCommunication {
public static class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s) {
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//同步块
synchronized (s) {
//等待唤醒机制
if(s.flag){//线程1判断是否有资源,有则等待,等待线程2消费完再生产
try {
s.wait();//等待,并且立即释放锁
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//无资源,则生产
if(x%2==0){
s.name="周杰伦";
s.age=29;
}else{
s.name="薛之谦";
s.age=26;
}
x++;
//生产完,修改标记
s.flag=true;
//唤醒正在wait的线程,即t2
s.notify();
}
//醒着的线程继续争抢CPU执行权,即t1、t2;
//若t1争抢到CPU执行权,则继续循环,根据标记flag判断,会进入等待,此时t2执行;
//若t2争抢到CPU执行权,根据标记flag判断,继续执行。
//所以,这个等待唤醒机制就保证了下一个一定是t2执行!!!
}
}
}
public static class GetThread implements Runnable{
private Student s;
public GetThread(Student s) {
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized (s) {
if(!s.flag){//线程2判断是否有资源,没有就等待,等待线程1生产资源
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//有资源,则消费
System.out.println(s.name+":"+s.age);
//消费完,修改标记
s.flag=false;
//唤醒正在wait的线程,即t1
s.notify();
}
//醒着的线程继续争抢CPU执行权,即t1、t2;
//同样,这个等待唤醒机制就保证了下一个一定是t1执行!!!
}
}
}
public static void main(String[] args) {
Student s=new Student();
Thread t1=new Thread(new SetThread(s));
Thread t2=new Thread(new GetThread(s));
t1.start();
t2.start();
}
}
输出:
周杰伦:29
薛之谦:26
周杰伦:29
薛之谦:26
周杰伦:29
薛之谦:26
周杰伦:29
薛之谦:26
周杰伦:29
薛之谦:26
周杰伦:29
薛之谦:26
周杰伦:29