上一篇我们说到,为何输出多次相同内容?
为何输出多次?
mike....man
mike....man
.......
丽丽....女女女女
丽丽....女女女女
原因: 两个线程互相争夺cpu使用权,输入线程抢夺到后持续一段时间输入,等到某一时刻被输出线程抢到cpu,开始疯狂输出一段时间。
那么,如何实现输入一个姓名性别,输出一个相应的姓名性别呢?
这就需要引入等待唤醒机制。
冻结:wait():该方法可以让线程处于冻结状态,并将线程临时存储到线程池中。
唤醒:notify():唤醒指定线程池中的任意一个线程。只唤醒一个。
notifyAll():唤醒指定线程池中的所有线程
另外,上述都使用在同步中,必须有互斥锁的情况下才可以使用
package contact;
class Res
{
String name;
String sex;
boolean flag = false;
public synchronized void set(String name,String sex)
{
if(flag)
try
{
this.wait();//等待线程都存放在线程池当中。
} catch (Exception e)
{
}
this.name = name;
this.sex = sex;
flag=true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try
{
this.wait();//等待线程都存放在线程池当中。
} catch (Exception e)
{
}
System.out.println(name+"......"+sex);
flag=false;
this.notify();
}
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.set("mike","man");
}
else
{
r.set("丽丽","女女女女");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
public class Contact
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
/*Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
*/
}
}
//显示结果
丽丽......女女女女
mike......man
package produceconsumer;
public class ProduceConsumer
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
if(flag)
try {
this.wait();
} catch (Exception e) {
}
this.name = name+"---"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try {
this.wait();
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()+"...++消费者++..."+this.name);
flag = false;
this.notify();
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("..商品..");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
//当只有一个生产者和一个消费者时,代码结果没有错误
Thread-0...生产者.....商品..---39913
Thread-1...++消费者++.....商品..---39913
Thread-0...生产者.....商品..---39914
Thread-1...++消费者++.....商品..---39914
但当出现多组生产者和消费者时,会出现错误。
//如果修改原代码,改为多组生产者和消费者
public class ProduceConsumer
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//发生错误:出现生产多个商品,消费一个商品的错误
Thread-1...生产者.....商品..---25601
Thread-0...生产者.....商品..---25602
Thread-2...++消费者++.....商品..---25602
Thread-1...生产者.....商品..---25603
Thread-0...生产者.....商品..---25604
Thread-2...++消费者++.....商品..---25604
使用while(flag)循环代替if(flag)判断标记,避免因为未判断标记出现的失误。让被唤醒的线程再一次判断标记。
使用notifyAll()代替notify(),唤醒全部线程。只使用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。
package produceconsumer;
public class ProduceConsumer
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag)
try {
this.wait();
} catch (Exception e) {
}
this.name = name+"---"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag = true;
this.notifyAll();
}
public synchronized void out()
{
while(!flag)
try {
this.wait();
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()+"...++消费者++..."+this.name);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("..商品..");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
//结果正确,生产者和消费者数量匹配
Thread-1...生产者.....商品..---34963
Thread-3...++消费者++.....商品..---34963
Thread-0...生产者.....商品..---34964
Thread-2...++消费者++.....商品..---34964
JDK1.5中提供了多线程升级解决方案,可在API中查询。
将同步Synchronized替换成现实Lock操作,将Object中的wait,notify,notifyAll,替换成了Condition对象。该对象可以使用Lock锁,进行获取。可以实现只唤醒对方操作。
package produceconsumer;
import java.util.concurrent.locks.*;
public class ProduceConsumer
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
/*
设置两个对象
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
*/
public void set(String name) throws InterruptedException
{
lock.lock();//获得锁
try
{ while(flag)
condition.await();//当前线程在接到信号或被中断之前一直处于等待状态
//condition_pro.await();
this.name = name+"---"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag = true;
condition.signalAll();//全部唤醒
//condition_con.signal();只唤醒对方操作
}
finally
{
lock.unlock();//释放锁
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition.await();//当前线程在接到信号或被中断之前一直处于等待状态
//condition_con.await();
System.out.println(Thread.currentThread().getName()+"...++消费者++..."+this.name);
flag = false;
condition.signalAll();//全部唤醒
//condition_pro.signal();只唤醒对方操作
}
finally
{
lock.unlock();//释放锁
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("..商品..");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
}