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 }
消费者类:
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 }
仓库类:
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; } }
测试类:
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 }
同步需要使用关键字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生产者——消费者 线程模型的主要内容,如果未能解决你的问题,请参考以下文章