生产消费者模型
基本概念:
生产消费者模型,就是存在两个线程,一个线程需要产生数据放入队列中,另一个对象需要取出数据,如果两者使用同一个缓冲区,同步处理的话,其效率就会大大减小,因为存在线程同步的问题需要处理。但是如果将生产者和消费者分开的话,生产者生产数据使用一个缓冲区,消费者使用数据用另一个缓冲区,若消费者的缓冲区中没有数据量,再把生产中的缓冲区的数据“倒”到消费者的缓冲区中,继续各自工作。如此,其效率便会大大提升。
线程同步:
要实现上述的功能,就要用到java中处理线程同步的一些类及方法。在使用这些方法之前需要了解JVM内部的结构。
如图所示:
虽然每个线程都会拥有以及创建一个属于自己的PC寄存器和JVM方法栈,但是方法区是被同一个JVM所有线程所共享的,因此两个线程同时调用相同方法时就会可能出错。
为了解决这个问题,我们就要用到java中锁的概念,若一个线程在调用这个方法时,,在该线程未使用完之前把这个方法锁起来。这时便用到关键字synchronized,代表这个方法加锁,相当于不管哪一个线程(例如生产者线程),运行到这个方法时,都要检查有没有其它线程正在用这个方法,有的话要等正在使用synchronized方法的线程运行完这个方法后再运行该生产者的线程,没有的话,就可以直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
如:public synchronized void producter(){…….方法体}
这样的话,将这整个方法都加上了一把“锁”,也就是说,一旦有一个线程在执行这个方法的时候就会将这整个方法都不能被其他线程所调用,虽然保险,然一般情况下会大大降低其线程的效率。因此,我们更多时候选择只将方法中其中一小部分易出现错误的地方锁住。
如:synchronized(syncObject) {
//允许访问控制的代码
}
这样就是将一个代码块放到里面,这块代码块在被执行时会被锁住。同时被使用的还有wait(),notify(),notifyAll().代码块可以调用wait()方法来将自身的操作挂起,直到同一个对象上的其他同步方法或同步代码块以某种方式将其改变,并调用notify()方法来通知此代码块改变已经完成。在加synchronized锁后才能调用wait(),notify(),和notifyAll().在wait()方法被调用后当前线程进入被加锁对象的线程休息室,然后释放锁,等待被唤醒。释放的锁由先前等待的另一个线程得到在获得锁后进行某种操作后通过notify或者notifyAll把原来线程从线程休息室唤醒,然后释放锁。原来被唤醒后,重新获取锁定,进行下一语句的执行。
有了上述的概念,我们就可以实现生产消费者模型。
双队列的主要代码:
- public class Tools {
- public static list<Phone>lT = new list<Phone>(100000);
- public static list<Phone>lP = new list<Phone>(100000);
- }
- public class DoubleBufferList {
- private List<Object> IP;
- private List<Object> IT;
- private int gap;
- /**
- * 构造方法
- *
- * @param lP
- * 用来存放对象的阻塞队列
- * @param lT
- * 用来取对象的阻塞队列
- * @param gap
- * 交换的间隔
- */
- public DoubleBufferList(List IP, List IT, int gap) {
- this.IP = IP;
- this.IT = IT;
- this.gap = gap;
- }
- public void check() {
- Runnable runner = new Runnable() {
- public void run() {
- while (true) {
- if (IT.size() == 0) {
- synchronized (IT) {
- synchronized (IP) {
- IT.addAll(IP);
- IP.notifyAll();
- }
- IP.clear();
- }
- }
- }
- }
- };
- Thread thread = new Thread(runner);
- thread.start();
- }
- }
public class Tools { public static list<Phone>lT = new list<Phone>(100000); public static list<Phone>lP = new list<Phone>(100000); } public class DoubleBufferList { private List<Object> IP; private List<Object> IT; private int gap; /** * 构造方法 * * @param lP * 用来存放对象的阻塞队列 * @param lT * 用来取对象的阻塞队列 * @param gap * 交换的间隔 */ public DoubleBufferList(List IP, List IT, int gap) { this.IP = IP; this.IT = IT; this.gap = gap; } public void check() { Runnable runner = new Runnable() { public void run() { while (true) { if (IT.size() == 0) { synchronized (IT) { synchronized (IP) { IT.addAll(IP); IP.notifyAll(); } IP.clear(); } } } } }; Thread thread = new Thread(runner); thread.start(); } }
这样的双缓冲队列比一个队列的线程的效率是大大提高的。注意:
生产者队列在队列不满时进行生产
消费者队列大小为0的时候进行提取,并且进行等待