java生产者——消费者 线程模型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java生产者——消费者 线程模型相关的知识,希望对你有一定的参考价值。

下面的4个文件体现了多线程的知识,以及线程同步(synchronized)的使用。其PopThread类对应消费者,PushThread类对应生产者,SafeStack对应存放资源的仓库。下面的TestSafeStack创建了1个生产者对象,1个存放资源的仓库对象,2个消费者对象。

消费者类:

技术分享
 1 /*
 2  * 通过实现Runnable接口实现线程
 3  */
 4 public class PushThread implements Runnable
 5 {
 6   /*
 7    * 创建一个SafeStack类的变量
 8    */
 9   private SafeStack s;
10   
11   public PushThread(){}
12   /*
13    * 通过有参的构造函数,传入临界资源
14    */
15   public PushThread(SafeStack s)
16   {
17     this.s = s;
18   }
19   
20   /**
21    * 重写run()方法
22    */
23   @Override
24   public void run()
25   {
26     int temp = 0;
27     
28     for(int i=0;i<100;i++)
29     {
30       /*
31        * 产生一个[0,10)的数字,并且保存到temp变量中
32        */
33       java.util.Random r = new java.util.Random();
34       temp = r.nextInt(10);
35       //调用临界资源中push方法
36       s.push(temp);
37       
38       /*
39        * 调用Thread.sleep()方法,让当前线程休眠0.1秒后再执行
40        */
41       try {
42         Thread.sleep(100);
43       }
44       catch(InterruptedException e){
45           e.printStackTrace();
46       }
47     }
48   }  
49 }
PushThread类

消费者类:

技术分享
 1 public class PopThread extends Thread {
 2     private SafeStack s;
 3 
 4     public PopThread() {
 5     }
 6 
 7     public PopThread(SafeStack s) {
 8         this.s = s;
 9     }
10 
11     public void run() {
12         for (int i = 0; i < 100; i++) {
13             int temp = s.pop();
14             System.out.println("->" + temp + "<-");
15             try {
16                 Thread.sleep(100);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20         }
21     }
22 }
PopThread类

仓库类:

技术分享
public class SafeStack
{
  private int top = 0;//下标
  /*
   * 用于存储产生的随机数整数
   */
  private int[] values = new int[10];//数组
  /*
   * 压栈和出栈的标志,通过dataAvailable变量控制push()方法和pop()方法中线程的等待。
   * dataAvailable的值默认是false,最开始让pop()方法中线程中等待。
   */
  private boolean dataAvailable = false;
  
  public void push(int n)//压栈方法 
  {
    /*
     * 使用synchronized关键字实现同步锁
     */
    synchronized(this)
    {
      /*
       * 如果dataAvailable的值是true,则让线程线程进入等待。
       */
      while(dataAvailable)//循环锁
      {//while是循环判断,if是单次判断
          try
        {//必须在synchronized语句块内调用wait
            wait();//等待,交钥匙后进等待池
        }
          catch(InterruptedException e)
        {
            e.printStackTrace();
        }        
      }
      values[top] = n;
      System.out.println("压入数字"+n+"步骤1完成");
      top++;//入栈完成
      if(top>=values.length){//当values数组满后才改变状态
          dataAvailable = true;//状态变为出栈
          notifyAll();//从等待池中唤醒所有线程
          System.out.println("压入数字完成");
      }
    }  //同步结束
  }

  public synchronized int pop() 
  {
      while(!dataAvailable)
      {
        try
        {
          wait();
        }
          catch(InterruptedException e)
        {
          e.printStackTrace();
        }        
      }  
      System.out.print("弹出");
      top--;//每一次取出一个数字
      int test=values[top];
      if(top<=0){//当数组中的所有数据都弹出完后才改变状态
          dataAvailable = false;
          //唤醒正在等待压入数据的线程
          notifyAll();
      }
      return test;
    }
  }
SafeStack类

测试类:

技术分享
 1 public class TestSafeStack{
 2     public static void main(String[] args){
 3         /*
 4          * 只创建一个临界资源
 5          */
 6         SafeStack s = new SafeStack();
 7         
 8         PushThread r1 = new PushThread(s);
 9         PopThread r2 = new PopThread(s);
10         PopThread r3 = new PopThread(s);
11         
12         /*
13          * 创建三个线程,一个PushThread和两个PopThread线程
14          */
15         Thread t1 = new Thread(r1);
16         Thread t2 = new Thread(r2);    
17         Thread t3 = new Thread(r3);
18         
19         t1.start();
20         t2.start();
21         t3.start();
22     }    
23 }
TestSafeStack类

同步需要使用关键字synchronized,语法格式为:

synchronized(对象引用){
//需要被同步的代码
}

如果需要同步整个run,则只需要在run方法上加上synchronized关键字即可

public synchronized void run() {...}

创建多线程也可以通过匿名内部类构造器来实现,构造器适合于创建对象的资源只使用一次。比如:

        new Thread(){//继承的构造器
            @Override
            public void run(){
                for(int i=0;i<10;i++){
                    System.out.println("<"+i+">");
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        Thread td=new Thread(new Runnable(){//实现接口的构造器
            @Override
            public void run() {
                for(int j=0;j<10;j++){
                    System.out.println(">"+j+"<");
                    try{
                      Thread.sleep(2000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        });
                td.start();//其中线程

 

当时上面的例子就不可以通过构造器来实现,因为在TestSafeStack.java中的创建的临界资源s

SafeStack s = new SafeStack()

被用于创建三个对象,就是:

        PushThread r1 = new PushThread(s);
        PopThread r2 = new PopThread(s);
        PopThread r3 = new PopThread(s);

所以涉及到临界资源的问题都不可以通过构造器来实现。

以上是关于java生产者——消费者 线程模型的主要内容,如果未能解决你的问题,请参考以下文章

Java实现多线程生产者消费模型及优化方案

Java线程同步模型-生产者与消费者

java生产者——消费者 线程模型

Java多线程14:生产者/消费者模型

java多线程笔记--生产者消费组模型

Java多线程的生产者与消费者模型,线程间的通信