LinkedBlockingQueue.take()似乎一直很忙

时间:2013-07-30 14:12:06

标签: java multithreading osgi producer-consumer

我正在尝试在 OSGi 中实现一项服务,该服务等待来自另一个捆绑包的传入数据并在收到数据时处理该数据。 我正在使用LinkedBlockingQueue,因为我不知道我将收到多少个数据包。 我的代码如下所示:

public class MyClass {

protected static LinkedBlockingQueue<JSONObject> inputQueue = new LinkedBlockingQueue<JSONObject>();
private ExecutorService workerpool = Executors.newFixedThreadPool(4);

public void startMyBundle() {
    start();
}

protected void start() {
    new Thread(new Runnable() {
        public void run() {
            while(true){
                workerpool.execute(new Runnable() {
                    public void run() {
                        try {
                            process(inputQueue.take());
                        } catch (InterruptedException e) {
                            System.out.println("thread was interrupted.");
                        }
                    }
                });
            }
        }
    }).start();
}

public void transmitIn(JSONObject packet) {
    try {
        inputQueue.put(packet);
    } catch (InterruptedException e) {

    }
}

protected  void process(JSONObject packet) {
    //Some processing
}

当我运行这个,并且只向服务发送一个数据包时,数据包首先被处理,但是我的处理器使用它的所有容量,大多数时候我看到OutOfMemoryError像这样:

java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "[Timer] - Periodical Task (Bundle 46) (Bundle 46)"

这可能是什么原因?

3 个答案:

答案 0 :(得分:1)

由于这些代码行,您将获得内存不足的异常:

while(true){
   workerpool.execute(new Runnable() {
   ...

这会永远创建新的Runnable实例并将它们添加到线程池的任务队列中。这些进入无限队列并快速填满内存。

我认为您需要在inputQueue.take()循环中调用while (true)的4个帖子。

for (int i = 0; i < 4; i++) {
    workerpool.execute(new Runnable() {
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                process(inputQueue.take());
            }
        }
    });
}
// remember to shut the queue down after you've submitted the last job
workerpool.shutdown();

此外,您不需要Thread将任务提交到线程池中。这是一种非阻塞操作,因此可以由调用者直接完成。

答案 1 :(得分:0)

这段代码是罪魁祸首:

protected void start() {
    new Thread(new Runnable() {
        public void run() {
            while(true){
                workerpool.execute(new Runnable() {
                    public void run() {
                        try {
                            process(inputQueue.take());
                        } catch (InterruptedException e) {
                            System.out.println("thread was interrupted.");
                        }
                    }
                });
            }
        }
    }).start();
}

它的作用是创建一个后台任务,向Runnable工作队列添加无限数量的ExecutorService。这最终导致了OOME。

我想你的意思是:

protected void start() {
    for (int i = 0; i < 4; ++i) {
        workerpool.execute(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        process(inputQueue.take());
                    } catch (InterruptedException e) {
                        //killed, exit.
                        return;
                    }
                }
            }
        });
    }
}

即。在等待输入的ExecutorService上运行4名工作人员。

答案 2 :(得分:0)

好吧,有点迂腐,但因为这是一个OSGi标记的问题......

  1. 清理 - 您正在创建线程和执行程序服务,但从不清理它。通常,您需要激活/停用一对方法,并且在停用后不留任何遗留物。从凝聚力的角度来看,您希望在一个对象中看到这一点,而不需要一个中心点来管理它。声明性服务是这种模式的理想选择。
  2. 分享 - 通常,您希望与其他人it is best to get an Executor from the service registry共享执行者。这将允许部署者根据系统中所有软件包的使用情况调整线程数。
  3. 还有一件事,鲍里斯给出了一个正确的解决方案,但它不是很有效,因为它总是占用4个线程和一个无界的LinkedQueue。更糟糕的是,代码像服务一样走路,它像服务一样讲话,但似乎并没有用作服务。我认为我们可以做得更好,因为队列+执行器有点加倍,在OSGi中这应该是一个服务。

    @Component
    public class JSONPackageProcessor implement TransmitIn {
      Executor executor;
    
      @Reference void setExecutor(Executor e) { this.executor = e; }
    
      public void transmitIn( final JSONPacket packet ) {
        executor.execute(new Runnable() {
           public void run() { 
             try { process(packet); } 
             catch(Throwable t) { log(packet, t); }
           }
        }
      }
    
      void process( JSONPacket packet ) { ... }
    }
    

    假设process(...)总是'很快'结束,这不需要清理。在此模型中,流不会像池中的(任意?)4个工作线程那样受到限制。 Executor的内部队列用于缓冲。您可以按如下方式对此进行限制:

      Semaphore throttle= new Semaphore(4)
    
      public void transmitIn( final JSONPacket packet ) throws Exception {
        throttle.acquire();
        executor.execute(new Runnable() {
           public void run() { 
             try { process(packet); } 
             catch(Throwable t) { log(packet, t); }
             finally { throttle.release(); }
        }
      }
    

    您甚至可以通过配置管理员轻松配置:

     @Activate void configure( Map<String,Object> map) throws Exception {
       if ( map.containsKey("throttle") )
         throttle = new Semaphore( map.get("throttle"));
     }
    

    这段代码的优点在于记录了大多数错误情况,关系之前/之后的并发性是正确的,因为您在OSGi中获得了保证。这段代码实际上可以正常工作(不保证某些拼写错误,实际上没有运行它)。

相关问题