1

我有一段类似于以下的代码:

final int THREADS = 11;  
BlockingQueue<Future<Long>> futureQueue = new ArrayBlockingQueue<Future<Long>>(THREADS);  
for (int i = 0; i < end; i++, count++) {  
    futureQueue.put(executor.submit(MyRunnable));  
}   
//Use queued results

我如何重构它以使其更加并发?我在这里监督有什么微妙之处吗?

更新:
每个 Runnable 都应该向服务器发送大量 HTTP 请求以进行压力测试。我在正确的轨道上吗?

4

2 回答 2

3

我会用

static final int THREADS = Runnable.getRuntime().availableProcesses();  

ExecutorService service = Executors.newFixedThreadPool(THREADS);

List<Future<Long>> futureQueue = new ArrayList<Future<Long>>(end);  
for (int i = 0; i < end; i++)
    futureQueue.add(executor.submit(new MyRunnable()));  

您正在使用有界队列,如果end > THREADS它会停止。

每个 Runnable 都应该向服务器发送大量 HTTP 请求以进行压力测试。我在正确的轨道上吗?

在这种情况下,我会使用以下代码,因为您的代码是 IO 而不是 CPU 绑定。

ExecutorService service = Executors.newCachedThreadPool();

如果你有超过 1000 个线程,你可能会从使用 NIO 中受益,但这只会让你的负载测试器更有效率,但它会使代码更加复杂。(如果你觉得这很难,那么编写高效且正确的 Selector 代码就更难了

在多台机器上运行测试仪会产生更大的差异。

于 2012-08-09T07:25:10.027 回答
3

在您的情况下,使用线程效果不佳。当你有一个 CPU 密集型工作时,线程池工作得很好。在您的情况下,您有一个 IO 密集型工作 - 它不受您拥有的 CPU 数量的限制,而是受您可以发送的网络数据包数量的限制。

在这种情况下,NIO 中的类是你的朋友。创建数百个连接并使用NIO 选择器查看哪个已准备好接收更多数据。

使用这种方法,您根本不需要线程;一个 CPU 内核足以填充 GBit 以太网连接 (~100MB/s)。

[编辑]当然,您可以创建数百个线程来尝试填充 IO 通道。但这有一些缺点:

  1. 线程由操作系统(或小型帮助程序库)管理。它们需要内存,每次切换线程时,CPU 都必须保存其状态并刷新其缓存。
  2. 如果一个线程只做少量的工作,线程切换可能比做工作更昂贵。
  3. 当您使用线程时,您会遇到所有常见的线程同步问题。
  4. 没有简单的方法可以确保您拥有正确数量的线程。如果线程太少,IO 通道将无法得到最佳使用。如果线程太多,通道将不会被最佳使用,因为线程会争夺访问权限。在这两种情况下,您都不能在开始测试后更改它。系统无法适应需求。

对于这样的任务,像Akka这样的框架更适合,因为它避免了所有这些问题,并且比线程更易于使用。

于 2012-08-09T07:33:19.890 回答