具有持久工作者实例的Threadpool

时间:2013-10-17 15:24:15

标签: java multithreading ftp threadpool threadpoolexecutor

我尝试将一个线程池中的任务排队,以便在工作人员获得空闲时立即执行,我已经找到了各种示例,但在所有情况下,示例都已设置为使用新的Worker实例对于每份工作,我都想要坚持不懈的工人。

我试图制作一个ftp备份工具,我有它工作,但由于单个连接的限制,它很慢。我理想的做法是有一个连接用于扫描目录和构建文件列表,然后是四个工作人员来下载所述文件。

以下是我的FTP工作人员的一个示例:

public class Worker implements Runnable {
  protected FTPClient _ftp;

  // Connection details
  protected String _host = "";
  protected String _user = "";
  protected String _pass = "";

  // worker status
  protected boolean _working = false;

  public Worker(String host, String user, String pass) {
    this._host = host;
    this._user = user;
    this._pass = pass;
  }

   // Check if the worker is in use
  public boolean inUse() {
    return this._working;
  }

  @Override
  public void run() {
    this._ftp = new FTPClient();
    this._connect();
  }

  // Download a file from the ftp server
  public boolean download(String base, String path, String file) {
    this._working   = true;
    boolean outcome = true;

    //create directory if not exists
    File pathDir = new File(base + path);
    if (!pathDir.exists()) {
      pathDir.mkdirs();
    }

    //download file
    try {
      OutputStream output = new FileOutputStream(base + path + file);
      this._ftp.retrieveFile(file, output);
      output.close();
    } catch (Exception e) {
      outcome = false;
    } finally {
      this._working = false;
      return outcome;
    }
  }

  // Connect to the server
  protected boolean _connect() {
    try {
      this._ftp.connect(this._host);
      this._ftp.login(this._user, this._pass);
    } catch (Exception e) {
      return false;
    }
    return this._ftp.isConnected();
  }

  // Disconnect from the server
  protected void _disconnect() {
    try {
      this._ftp.disconnect();
    } catch (Exception e) { /* do nothing */ }
  }
}

我希望能够在工作人员可用时为队列中的每个任务调用Worker.download(...),而无需为每次下载创建与ftp服务器的新连接。

任何帮助都会受到赞赏,因为我之前从未使用过线程,而且我现在正在围成一圈。

2 个答案:

答案 0 :(得分:4)

  

示例已经设置为每个作业使用一个新的Worker实例,我想要持久的工作者。

这是一个常见问题,有几种不同的解决方案。你想要的是一些上下文每个线程,而不是每个RunnableCallable将提交给ExecutorService

一种选择是拥有ThreadLocal来创建ftp个实例。这不是最佳的,因为在线程终止时没有简单的方法来关闭ftp连接。然后,您可以通过限制线程池中运行的线程数来限制连接数。

我认为更好的解决方案是仅使用ExecutorService分叉您的工作线程。对于每个工人,向他们注入一个BlockingQueue,他们都用它们出列并执行他们需要做的任务。这与ExecutorService内部使用的队列分开。然后,您可以将任务添加到您的队列,而不是ExecutorService本身。

private static final BlockingQueue<FtpTask> taskQueue
        = new ArrayBlockingQueue<FtpTask>();

所以你的任务对象会有类似的东西:

public static class FtpTask {
     String base;
     String path;
     String file;
}

然后run()课程中的Worker方法会执行以下操作:

public void run() {
    // make our permanent ftp instance
    this._ftp = new FTPClient();
    // connect it for the life of this thread
    this._connect();
    try {
        // loop getting tasks until we are interrupted
        // could also use volatile boolean !shutdown
        while (!Thread.currentThread().isInterrupted()) {
            FtpTask task = taskQueue.take();
            // if you are using a poison pill
            if (task == SHUTDOWN_TASK) {
                break;
            }
            // do the download here
            download(task.base, task.path, task.file);
        }
    } finally {
        this._disconnect();
    }
}

同样,您可以通过限制线程池中运行的线程数来限制连接数。

  

我理想的做法是拥有一个用于扫描目录和建立文件列表的连接,然后是四个工作人员来下载所述文件。

我会有一个Executors.newFixedThreadPool(5);并添加一个执行扫描/构建的线程和4个正在进行下载的工作线程。当工作线程从同一队列中获取时,扫描线程将放入BlockingQueue

答案 1 :(得分:2)

我建议根据要求选择核心大小和maxpoolsize的ThreadPoolexecutor。在这种情况下也使用链接阻塞队列,它将以FIFO方式执行您的任务。

一旦线程(工作者)变为空闲,任务将从队列中挑选并执行。

查看ThreadPoolExecutor的详细信息。如果你在ThreadPoolexecutor的实现中陷入困境,请告诉我。