java的并发是一个复杂的主题,信手拈来就可以发现一大堆的有趣的问题。这里不讨论生产者模式,主要关注如何用java来实现该模式;
这个例子是由生产者生产随机数,多个线程来取随机数;
package ThreadTest; public class SetOfNum { private int numOne;//标记资源是否可用 private int numTwo;//资源的内容 public int getNumOne() { return numOne; } public void setNumOne(int numOne) { this.numOne = numOne; } public int getNumTwo() { return numTwo; } public void setNumTwo(int numTwo) { this.numTwo = numTwo; } SetOfNum(int a, int b){ numOne = a; numTwo = b; } }
接着用一个resources类来作为缓冲区资源;
package ThreadTest; public class Resources { private SetOfNum num; private static Resources res; private Resources(SetOfNum num){ this.num = num; } public SetOfNum getNum() { return num; } public void setNum(SetOfNum num) { this.num = num; } public static Resources getInstance(SetOfNum num){ if(res == null){ res = new Resources(num); } else res.setNum(num); return res; }//这里是自己实现singlon模式; synchronized public int getRes(){ try{ if(this.num.getNumOne() == -1){ wait(); } }catch(Exception e){ e.printStackTrace(); } this.getNum().setNumOne(-1); return this.getNum().getNumTwo(); } synchronized public void setRes(){ try{ if(this.num.getNumOne() != -1){ notifyAll(); return; } }catch(Exception e){ e.printStackTrace(); } this.num.setNumOne(1); this.num.setNumTwo((int)(Math.random()*1000)); } }
这里有三个有趣的问题:
1,为什么要对包含wait()和notify()的函数进行synchronized?
因为这里出现了典型的read and then change 的操作,那么在并发的时候就可能出现竞争条件,所以必须同步;
2,为什么作为线程的行为,wait()和notify()要放在object中而不是thread中呢?
这里wait(),notify()本质上是对资源加锁,而资源本身不是线程,而是某个object。这里我认为java库的开发者可能在实现的时候发现与其在对需要对某个资源进行加锁的操作(如果多次加锁,就要多次操作,很容易出错的),不如将操作直接写在资源上,也就是object上。
3,wait(),notify()和synchronized 之间有什么关系?
其实都没有什么关系,synchronized 本质是提供互斥锁(mutex)功能,而wait(),notify()是提供了线程间的通信;
接着是:
package ThreadTest; public class Producer extends Thread{ private Resources res; private int count = 20; Producer(Resources res){ this.res = res; } public void run(){ while(true){ this.res.setRes(); count --; } } }
package ThreadTest; public class Consumer extends Thread { private Resources res; private int conId; private int count = 3; Consumer(Resources res, int conId) { this.res = res; this.conId = conId; } public void run() { while (count > 0) { int a = res.getRes(); System.out.println("this is consumer " + this.conId + "!"); System.out.println("get the num is " + a + "!"); count --; } } }
package ThreadTest; public class MainThread { public static void main(String arg[]){ SetOfNum num = new SetOfNum(-1,1); Resources res =Resources.getInstance(num); Producer pro = new Producer(res); //pro.start(); Consumer[] con = new Consumer[10]; for(int i = 0; i < 10 ; i++){ con[i] = new Consumer(res,i); con[i].start(); } pro.start(); System.out.println("in main thread!"); } }
这里的pro可以在不同地方试一下:(start是立即返回的)就会发现for循环的每一次迭代其实花费了很多时间,在main thread一次迭代的时间都可以被用来做多次的线程切换(输出从结果中得到)。