我构建了一个网络爬虫,但它是单线程的。现在我正在扩展它以使用多个线程。我无法理解以下内容:
- 我应该创建多少线程?它应该是一个固定的数字还是一个动态的,根据持有 URI 的队列的长度而变化?(也考虑到可用内存)
- 我已经通过 Runnable 接口为线程创建了一个新类,我希望每个线程的
run
方法都能访问我在 Main 类中创建的正在调用thread.start()
. 我应该如何从每个线程访问这个对象?
我正在使用 NetBeans。
我构建了一个网络爬虫,但它是单线程的。现在我正在扩展它以使用多个线程。我无法理解以下内容:
run
方法都能访问我在 Main 类中创建的正在调用thread.start()
. 我应该如何从每个线程访问这个对象?我正在使用 NetBeans。
对于第一个问题,我猜在您的情况下,最好使用动态调整的线程池,例如:
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));
}
你肯定会想要一个网络爬虫的并发性:)
而且您可能会想要设置一个线程池,以便您可以重用线程,而不会花费为每个任务实例化新线程的成本。
您拥有的线程池选项是 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 关键字或使用线程安全数据类型。
希望这可以帮助。祝你好运!!
不可能有任何具体的答案。但是您可以学习以下内容-
对于第 1 点研究ExecutorService and ThreadPoolExecutor
。
对于第 2 点研究callable and Future
。