【问题标题】:Controlling the number of threads and controlling object accessing in a Multi Threaded Web Crawler in Java在 Java 中的多线程 Web 爬虫中控制线程数和控制对象访问
【发布时间】:2012-10-04 07:01:43
【问题描述】:

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

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

我正在使用 NetBeans。

【问题讨论】:

  • 1.使用线程池,您不必携带线程数。您可以根据当前的 cpu 数量创建它们。 (更准确的信息,-> anserws)

标签: java multithreading web-crawler


【解决方案1】:

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

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));
}

【讨论】:

  • +1 好答案。不过,我想向他推荐使用 FixedThreadPool。我猜爬行并不是真的那么“短暂”。因此,即使通过 ExecutorService 一次创建数百个线程也可能会有所帮助。
  • @Fildor:我实际上并不是很清楚每个任务在网络爬虫中会做什么。我的印象是它只需要一个链接,提取它指向​​的链接,将它们放入全局队列然后停止,这就是为什么我期望它们是短暂的。
  • 只是想添加 FixedThreadPool 以防它不是那么短暂。并不是要冒犯 :) 假设它短暂的,你的答案是完全正确的。
  • @Fildor:啊,不,我没有被冒犯,很抱歉,如果结果是这样。 :) 实际上我只是说我并不真正了解他是如何实现爬虫的。
  • @Tudor。嘿,谢谢它的帮助,在我的情况下,固定池将是一个不错的选择。我对第二个答案有疑问,我的对象将非常大,因此为每个线程处理它需要时间。是否有任何其他选项或只是传递一个指向它的指针。
【解决方案2】:

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

您可能会想要设置一个线程池,以便您可以重用线程,而不必为每个任务实例化新线程而付出代价。

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

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

只需通过调用以下方法之一来创建ExecutorService

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

一旦有了线程池,您就可以使用 submit() 方法调用单个可运行对象(不返回响应)或 callable(确实如此)。

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

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

然后得到结果:

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

如果您希望多个线程能够修改同一个对象,您需要使用synchronized keyword in the setters 或使用thread-safe data types

希望这会有所帮助。祝你好运!!

【讨论】:

  • +1 用于突出动态池的问题 - 可以快速生成大量链接,从而导致线程爆炸。一个固定的池会更好,(一个大的,'因为网络阻塞,但固定)。
【解决方案3】:

没有具体的答案。但是你可以学习以下 -

关于ExecutorService and ThreadPoolExecutor 的第一点研究。

关于callable and Future的第二点研究。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多