天天看点

学习和理解JAVA线程同步--生产者与消费者例子

学习和理解JAVA线程同步--生产者与消费者例子

JAVA线程同步通常需要使用sychronized对临界资源进行加锁,所谓的临界资源就是指这些线程共同使用的资源。

sychronized通常是放在方法名前,这是表示这个方法是同步的,实际上是对this加锁。

或者放在一个对象前,对某个共同使用的对象加锁。

生产者和消费者的例子是非常经典的,这里需要定义一个池子,用于往里放入产品。定义生产者不断生产产品,当池子不满的情况下可以继续放,满了则等待。

消费者一直从池子取,池子空则等待。

我们使用实现runnable来实现生产者和消费者。

写一个同步栈作为池子,对push和pop方法进行同步。

代码如下:

[java]  view plain copy print ?

  1. class SycnStack{  
  2.     private Integer index=0;  
  3.     private char[] data;  
  4.     public SycnStack(int num){  
  5.         data = new char[num];  
  6.     }  
  7.     public void push(char c){  
  8.         synchronized (this) {//对index加锁为何不行???必须this  
  9.             while(index==data.length-1)   
  10.             {  
  11.                 System.out.println("池子已经满,不能放入了!"+Thread.currentThread().toString());  
  12.                 try {  
  13.                     this.wait();  
  14.                 } catch (InterruptedException e) {  
  15.                     e.printStackTrace();  
  16.                 }  
  17.             }  
  18.             this.notify();//获取锁  
  19.                 data[index]=c;  
  20.                 System.out.println("放入"+c+" "+Thread.currentThread().toString());  
  21.                 index++;  
  22.         }  
  23.     }  
  24.     public Character pop(){  
  25.         synchronized (this) {  
  26.             while(index==0){  
  27.                 System.out.println("池子已经空了,取不了了!"+Thread.currentThread().toString());  
  28.                 try {  
  29.                     this.wait();  
  30.                 } catch (InterruptedException e) {  
  31.                     e.printStackTrace();  
  32.                 }  
  33.             }  
  34.             this.notify();  
  35.                 index--;  
  36.                 char c = data[index];  
  37.                 System.out.println("取出"+c+" "+Thread.currentThread().toString());  
  38.                 return c;  
  39.         }  
  40.     }  

然后写生产者和消费者,其run方法就是不断的生产产品放入池子或取出产品:

[java]  view plain copy print ?

  1. class Producer implements Runnable{  
  2.     private SycnStack stack;  
  3.     public Producer(SycnStack stack){  
  4.         this.stack = stack;  
  5.     }  
  6.     @Override  
  7.     public void run() {  
  8.         int i=10;  
  9.         while(i-->0){  
  10.             stack.push( (char)(26*Math.random()+'A'));  
  11.             Thread.yield();  
  12.         }  
  13.     }  
  14. }  
  15. class Consumer implements Runnable{  
  16.     private SycnStack stack;  
  17.     public Consumer(SycnStack stack){  
  18.         this.stack = stack;  
  19.     }  
  20.     @Override  
  21.     public void run() {  
  22.         int i=10;  
  23.         while(i-->0){  
  24.             stack.pop();  
  25.             Thread.yield();  
  26.         }  
  27.     }  
  28. }  

main方法如下,创建一个栈用于放产品,然后创建生产者和消费者都用这个产品栈,并启动:

[java]  view plain copy print ?

  1. public class ProductorConsumer {  
  2.     public static void main(String[] args){  
  3.         SycnStack s = new SycnStack(5);  
  4.         new Thread(new Producer(s)).start();  
  5.         new Thread(new Consumer(s)).start();  
  6.     }  
  7. }  

结果如下:

[plain]  view plain copy print ?

  1. 放入G Thread[Thread-0,5,main]  
  2. 放入L Thread[Thread-0,5,main]  
  3. 放入D Thread[Thread-0,5,main]  
  4. 放入K Thread[Thread-0,5,main]  
  5. 池子已经满,不能放入了!Thread[Thread-0,5,main]  
  6. 取出K Thread[Thread-1,5,main]  
  7. 放入P Thread[Thread-0,5,main]  
  8. 池子已经满,不能放入了!Thread[Thread-0,5,main]  
  9. 取出P Thread[Thread-1,5,main]  
  10. 放入E Thread[Thread-0,5,main]  
  11. 池子已经满,不能放入了!Thread[Thread-0,5,main]  
  12. 取出E Thread[Thread-1,5,main]  
  13. 放入M Thread[Thread-0,5,main]  
  14. 池子已经满,不能放入了!Thread[Thread-0,5,main]  
  15. 取出M Thread[Thread-1,5,main]  
  16. 放入P Thread[Thread-0,5,main]  
  17. 池子已经满,不能放入了!Thread[Thread-0,5,main]  
  18. 取出P Thread[Thread-1,5,main]  
  19. 放入Q Thread[Thread-0,5,main]  
  20. 池子已经满,不能放入了!Thread[Thread-0,5,main]  
  21. 取出Q Thread[Thread-1,5,main]  
  22. 放入L Thread[Thread-0,5,main]  
  23. 取出L Thread[Thread-1,5,main]  
  24. 取出D Thread[Thread-1,5,main]  
  25. 取出L Thread[Thread-1,5,main]  
  26. 取出G Thread[Thread-1,5,main]  

再看同步栈的代码第十行,是对this加锁,指的就是对当前这个栈加锁。

之前我测试使用对index加锁,发现是会报错的,我想可能是因为临界资源不仅是index,还有data,如果只对index加锁,当一个线程在用栈的时候,另一个线程仍然能够使用这个栈,就会造成index的混乱之后就报错了。

如果谁有更详细的解释也可以告诉我。