2

我构建了一个网络爬虫,但它是单线程的。现在我正在扩展它以使用多个线程。我无法理解以下内容:

  1. 我应该创建多少线程?它应该是一个固定的数字还是一个动态的,根据持有 URI 的队列的长度而变化?(也考虑到可用内存)
  2. 我已经通过 Runnable 接口为线程创建了一个新类,我希望每个线程的run方法都能访问我在 Main 类中创建的正在调用thread.start(). 我应该如何从每个线程访问这个对象?

我正在使用 NetBeans。

4

3 回答 3

4

对于第一个问题,我猜在您的情况下,最好使用动态调整的线程池,例如:

ExecutorService exec = Executors.newCachedThreadPool();

创建一个线程池,根据需要创建新线程,但在可用时将重用以前构造的线程。这些池通常会提高执行许多短期异步任务的程序的性能。如果可用,对执行的调用将重用以前构造的线程。如果没有可用的现有线程,将创建一个新线程并将其添加到池中。六十秒内未使用的线程将被终止并从缓存中删除。因此,保持空闲足够长时间的池不会消耗任何资源。

对于第二个问题,您可以创建一个构造函数并以这种方式传递对象:

class ThreadTask implements Runnable {
     private Object obj;

     public ThreadTask(Object obj) {
         this.obj = obj;
     }

     public void run() {
     }
}

public static void main(String[] args) {
     Object obj = new Object();
     exec.submit(new ThreadTask(obj));
}
于 2012-10-04T07:28:42.337 回答
3

你肯定会想要一个网络爬虫的并发性:)

而且您可能会想要设置一个线程池,以便您可以重用线程,而不会花费为每个任务实例化新线程的成本。

您拥有的线程池选项是 FixedThreadPool 和 CachedThreadPool。Java 并发教程中详细解释了其中每一个的好处。CachedThreadPool 的一大缺点是可以创建的线程数没有限制。如果将大量线程添加到池中,您可能会看到一些显着的性能下降或超时(如果您定义了套接字超时)。

无论哪种情况,设置线程池的最佳实践是通过java.util.concurrent.Executors

这只是通过调用以下之一来创建ExecutorService的问题:

ExecutorService threadPool = Executors.newCachedThreadPool();
ExecutorService threadPool = Executors.newFixedThreadPool(500); 

拥有线程池后,您可以使用 submit() 方法调用单个可运行对象(不返回响应)或可调用对象(返回响应)。

如果您使用可调用对象生成期货,也可以运行 .invokeAll() :

futures = cachedThreadPool.invokeAll(tasks,
                                     timeout,
                                     TimeUnit.MILLISECONDS);

然后得到结果:

for (Future f: futures) {
   someList.add(f.get())
}

如果您希望多个线程能够修改同一个对象,则需要在 setter 中使用 synchronized 关键字或使用线程安全数据类型

希望这可以帮助。祝你好运!!

于 2012-10-04T07:40:52.140 回答
1

不可能有任何具体的答案。但是您可以学习以下内容-

对于第 1 点研究ExecutorService and ThreadPoolExecutor

对于第 2 点研究callable and Future

于 2012-10-04T07:22:06.317 回答