当多个线程操作同一个资源,但是操作的动作不同时,就会需要线程间进行通信。很著名的是生产者消费者的例子。
有一个盘子,只能放一片面包,生产者生产面包放入盘子,消费者从盘子中取走面包吃掉。
由简单开始,i+1。先看一个生产者、一个消费者。
代码如下:
public class ProducerConsumerDemo {
public static void main(String[] args){
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).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;
notify();
}
public synchronized void out(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
flag = false;
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和Tread-1两个线程是交替进行,生产者生产商品i,消费者就把商品i消费掉。然后生产者生产商品i+1,消费者再消费商品i+1。
本来我自己想的实现过程是这样的:对于盘子来说,它有两种状态,可以往里放面包和不可以放面包。生产者来了,如果可放,就生产一个面包,并把盘子置为不可放面包的状态,如果不可放,就什么都不操作。消费者来了,如果可放(就代表不能取),就什么都不操作,如果不可放(代表能取),就取走一个面包,并把盘子置为可放面包状态。代码如下:
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){}
else{
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
flag = true;
}
//notify();
}
public synchronized void out(){
if(!flag){
}
//try{this.wait();}catch(Exception e){}
else{
System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
flag = false;
}
//notify();
}
}
与上面的示例中代码的区别是没有使用wait()和notify()方法。一样能实现效果。看图:
不用使用wait()和notify()与使用有什么区别呢?既然它存在,肯定是有它的道理的。猜测它的优点是效率更高。用什么方法可以验证一下?尝试着加了运行时间和打印输出。如图:
(1)不用wait()/notify()
(2)用wait()/notify()
count为10000时,不使用wait()和notify()生产9999个面包需要1330ms,而使用wait()和notify()生产9999个面包只需要406ms。多次执行,每次的结果相差不大。
增加一下数量级,再比比,也很明显:6704msVS 3208ms。
(1)不用wait()/notify()(2)用wait()/notify()
计时代码增加到了main方法所在类和公共资源类下,代码如下:
public class ProducerConsumerDemo {
public static long strateTime;
public static void main(String[] args){
Resource r = new Resource();
strateTime = System.currentTimeMillis();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{ //公共资源
public static long endTime;
private String name;
private int count =1;
private boolean flag = false;
public synchronized void set(String name){
if(flag)
// {
// System.out.println("无效执行");
// }
try{this.wait();}catch(Exception e){}
else{
this.name = name + "--" + count++;
if(count==100000){
endTime= System.currentTimeMillis();
System.out.println("程序运行时间:" + ( endTime - ProducerConsumerDemo.strateTime )+"ms");
}
System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
flag = true;
}
notify();
}
public synchronized void out(){
if(!flag)
// {
// System.out.println("无效执行");
// }
try{this.wait();}catch(Exception e){}
else{
System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
flag = false;
}
notify();
}
}
对比发现,差不多两倍的差距,效率是不一样的。是因为啥呢?
//TODO:
多线程方法汇总:
等待唤醒:wait()、notify()、notifyAll()。
waite()是把线程由运行状态置为等待状态,等待线程都存在线程池中。
notify()方法是把等待状态的线程从线程池中唤醒。通常唤醒线程池中的第一个等待的线程。
notifyAll()是把线程池中的所有等待线程都唤醒。