Java 使用ArrayBlockingQueue会使进程变慢

Java 使用ArrayBlockingQueue会使进程变慢,java,multithreading,threadpoolexecutor,blockingqueue,Java,Multithreading,Threadpoolexecutor,Blockingqueue,我最近在多线程进程中使用了ArrayBlockingQueue。但它似乎放慢了速度,而不是加快了速度。你们能帮帮我吗?我基本上是导入一个文件(大约30万行)并解析它们并将它们存储在数据库中 public class CellPool { private static class RejectedHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable

我最近在多线程进程中使用了ArrayBlockingQueue。但它似乎放慢了速度,而不是加快了速度。你们能帮帮我吗?我基本上是导入一个文件(大约30万行)并解析它们并将它们存储在数据库中

public class CellPool {
private static class RejectedHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable arg0, ThreadPoolExecutor arg1) {
      System.err.println(Thread.currentThread().getName() + " execution rejected: " + arg0);     
    }
  }

  private static class Task implements Runnable {
    private JSONObject obj;

    public Task(JSONObject obj) {
      this.obj = obj;
    }

    @Override
    public void run() {
      try {
        Thread.sleep(1);
        runThis(obj);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    public void runThis(JSONObject obj) {
        //where the rows are parsed and stored in the DB, etc
    }
  }

  public static void executeCellPool(String filename) throws InterruptedException {
    // fixed pool fixed queue
    BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(300000, true);
    ThreadPoolExecutor executor = new ThreadPoolExecutor(90, 100, 1, TimeUnit.MINUTES, queue);

    DataSet ds = CommonDelimitedParser.getDataSet(filename);
    final String[] colNames = ds.getColumns();
    while (ds.next()) {
        JSONObject obj = new JSONObject();
        //some JSON object
        Task t = new Task(obj);
        executor.execute(t);
    }
  }
公共类单元池{
私有静态类RejectedHandler实现RejectedExecutionHandler{
@凌驾
public void rejectedExecution(可运行arg0,线程池执行器arg1){
System.err.println(Thread.currentThread().getName()+“执行被拒绝:”+arg0);
}
}
私有静态类任务实现可运行{
私有JSONObject对象;
公共任务(JSONObject对象){
this.obj=obj;
}
@凌驾
公开募捐{
试一试{
睡眠(1);
runThis(obj);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
public void runThis(JSONObject obj){
//行被解析并存储在数据库中,等等
}
}
公共静态void executeCellPool(字符串文件名)引发InterruptedException{
//固定池固定队列
BlockingQueue=new ArrayBlockingQueue(300000,真);
ThreadPoolExecutor executor=新的ThreadPoolExecutor(90100,1,TimeUnit.MINUTES,queue);
DataSet ds=CommonDelimitedParser.getDataSet(文件名);
最终字符串[]colNames=ds.getColumns();
while(ds.next()){
JSONObject obj=新的JSONObject();
//一些JSON对象
任务t=新任务(obj);
执行人,执行人(t);
}
}

}

如果您想尽快将文件中的记录持久化到关系数据库中,您应该使用JDBC批量插入,而不是逐个插入记录。

tl;dr较大的队列大小可能会产生负面影响,较大的线程数也会产生负面影响。理想情况下,您希望您的消费者和生产者以类似的速度工作

添加队列导致问题的原因是,您使用的队列非常大(这是不必要的),占用了资源。通常,阻塞队列在队列中没有剩余空间时阻塞生产者,在队列中没有剩余对象时阻塞消费者。通过创建一个如此大的静态大小的空间,Java在内存中分配了这个空间,而您几乎肯定不会全部使用它。如果你的消费者速度太慢,强迫你的制作人等待队列中的空间清空会更有效。您不需要同时将文件中的所有行存储在队列中

javadoc中讨论了线程池执行器队列

有界队列。有界队列(例如,ArrayBlockingQueue)在与有限的MaximumPoolSize一起使用时有助于防止资源耗尽,但可能更难调整和控制。队列大小和最大池大小可以相互权衡:使用大型队列和小型池可以最大限度地减少CPU使用、操作系统资源和上下文切换开销,但也可能导致人为的低吞吐量。如果任务经常阻塞(例如,如果它们是I/O绑定的),系统可能能够为更多的线程安排时间,而不是您允许的时间。使用小队列通常需要更大的池大小,这使CPU更繁忙,但可能会遇到不可接受的调度开销,这也会降低吞吐量

您的大线程大小为90,加上非常大的池大小为300000,很可能会占用大量内存,从而导致额外的线程调度开销。我会大大降低他们两个。我不知道您运行的是什么硬件,但因为听起来您正在编写一个IO密集型程序,所以我会尝试将您的CPU可以处理的线程数增加一倍,并对阻塞队列的大小进行调整,以了解哪些是有效的(注意:我还没有对此进行研究,这是基于我运行队列和执行器的经验。很高兴其他人提出不同的计数!)

不过,需要注意的是,如果队列太小,如果添加到队列失败,
execute()
方法将抛出一个
RejectedExecutionException
。监视队列的一种方法是在计划任务之前检查其容量。您可以通过调用:

executor.getQueue().remainingCapacity()
不要使用
executor.getQueue()
方法以任何方式更改队列,但它可以用于监视

另一种方法是在没有定义容量的情况下使用无界队列,例如
LinkedBlockingQueue
。这样,您就不需要处理队列大小。但是,如果生产者的运行速度比消费者快得多,您将再次面临占用太多内存的问题


另外,克斯特亚是对的,JDBC批量插入会更快。

相对于什么?使用另一种类型的
阻塞队列
?请摆脱睡眠。为什么?我建议你摆脱队列、线程、执行器,所有这些,并在单个线程中作为一个批来完成。你不需要一个包含30000项和90-100项的队列阅读此内容。
Thread.sleep(1);
在您的代码中没有任何意义。如果可以的话,您应该始终避免使用sleep。谢谢,AndyN和@kostya!我是java新手。我尝试了批插入,速度明显更快。:)