很久没有怎么写过多线程模型的东西了.刚好 最近修改了一个异步逻辑的时候, 将之前的线程轮询方式修改为 等待-通知 形式, 经过简单测试, 其吞吐量得到了非常大的提升. 现不敢独享,分享之, 以飨读者.同时也好做一个知识的回顾.先简单介绍一下业务逻辑, 很简单, 每个生产逻辑线程在 得到一个需要待处理的数据之后, 封装之并作为一个对象塞入到一个 业务对象池中.(下文我们对这些线程, 称之为生产者)然后另外一个(或者多个)消费线程不断的轮询这个 业务对象池, 看是否存在未处理对象, 若有就提取一个出来做相应的处理并从池中清除. 如果没有,则线程会sleep一会儿.(下文我们对这种线程, 称之为消费者)这个轮询的模型在业务量比较小的时候是没有问题的,而且实现也很简单.但是如果生产者涌入数量非常多而且非常快, 这个时候消费者的处理能力就会急剧下降.
 轮询方式的模型: 共享的对象池 pool , 标黑的为synchronzied同步块.消费者:            while (true) {                Object obj = null;                    // 待处理对象
                synchronized (pool ) {
                    if (pool .size() >= 1) {
                        obj = pool.get(0);        // 对象池中提取一个待处理对象.例如list就是remove(0)
                    } else {
                        obj = null;
                    }
                }
                if (obj == null) {
                    Thread.sleep(10);                // 如果没有, 则会暂停一会儿. 统一为10ms
                } else {
                    doSomething(obj);               //  处理这个对象
                }            }
生产者:
     
        synchronized (pool ) {
            pool .add(object);                       // 塞入一个待处理的对象.
        }
     
等待-通知模型:消费者:            while (true) {
                Object obj = null;
                
           synchronized (pool ) {
                    while (pool .size() <= 0) {    // 注意这里必须用while而不能用if 判断,否则会产生所谓的 虚假唤醒 (spurious wakeup)。
                        pool .wait();              // 等待通知
                    }
                    obj = pool .get(0);                
                }
                doSomething(obj);                       // 如果不放心这里还可以为空判断, 但是理论上应该不可能出现.(pool中没有空对象的话)
            }
生产者: 
        synchronized (pool ) {
            pool .add(input);
            pool .notify();                                // 唤醒等待的线程
        }
 经过测试.在生产者和消费者都较多的情况下, 等待-通知模型比轮询方式吞吐量是具有数量级的优势的, 但是当都很小的时候这两种方式的区别并不大, 这个是可以理解的.毕竟等待的线程被唤醒之后也是需要平等的同其他线程 一起来争取对 对象池pool的使用权, 同时我也发现, 在 标准的生产者-消费者模型中, 其系统吞吐量比目前这种等待-通知模型的吞吐量又提升了一个数量级. 开始的时候稍微有点迷惑, 后来简单想想,其实这样也对, 在标准的模型中,每当消费者发现没有数据的时候.会立即通知生产者进行生产, 两者之间是主动的. 而在实际业务模式中,没有用户访问我们的系统, 也就无从生产数据了. 是一个被动的过程. 所以其吞吐量比较低也是可以理解的.

解决方案 »

  1.   

    附 一个标准的生存者消费者模型: 
       static final  Pool pool // 需要同步访问的共享资源 // 生产者代码 
    … produce() {   while(true) {    synchronized(pool ){ 
          pool.add(object) // 产生一些东西,放到 pool 共享资源中       pool .notify(); //然后通知消费者 
          pool .wait(); // 然后自己进入pool待召队列 
      } 
      } 
    } // 消费者代码 
    … consume() {   while(true) {    synchronized(pool ){ 
           while(condition is notTrue){   // 必须使用while判断条件是否满足.
          pool .wait(); // 进入pool 待召队列,等待生产者的通知 
              }
          // 读取pool 共享资源里面的东西并做一些逻辑处理操作
             pool .getObject();      pool .notify(); // 然后通知生产者 
      } 
      }