在Java中运行多少个线程?

5
我有个绝妙的想法,可以加快生成36个文件所需的时间:使用36个线程!但是,如果我使用一个连接(一个连接对象)和36个线程/会话启动一个连接,一切都比逐个执行每个线程要慢得多。现在,如果我尝试创建36个新连接(36个连接对象),那么每个线程都有一个单独的连接到服务器,要么出现内存不足异常(某种方式程序仍在运行,并成功结束其工作,比逐个执行每个线程时慢)。那么该怎么办?如何找到最佳的线程数?因为在启动我的36个线程之前,是3?我正在使用联想笔记本电脑Intel Core i5。

你是在j2ssh连接的客户端还是服务器端运行工作线程? - Paŭlo Ebermann
@Paŭlo Ebermann 在服务器上运行它们,thread == 向服务器发送2个命令。 - user615927
听起来你需要检查一下JVM是否已经耗尽了内存。 - Thorbjørn Ravn Andersen
9个回答

14

您可以使用 ExecutorService 将线程数缩减至更合理的数量。您可能希望使用接近可用处理器核心数的数字,例如:

int threads = Runtime.getRuntime().availableProcessors();
ExecutorService service = Executors.newFixedThreadPool(threads);
for (int i = 0; i < 36; i++) {
    service.execute(new Runnable() {
        public void run() {
            // do what you need per file here
        }
    });
}
service.shutdown();

你看,这里的完成时间大约是30分钟,我猜测36个操作中的每一个都是依次创建和执行的,thread1.start;thread1.end;thread2.start;thread2.end..thread36.end... 我个人期望至少在12分钟内完成执行,而不是30分钟,现在可以通过增加线程数(Executors.newFixedThreadPool(threads);)来提高速度吗? - user615927

5

一个好的做法是生成与您处理器中核心数量相当的线程。我通常使用 Executors.fixedThreadPool(numOfCores) 执行服务,并从我的作业队列中不断提供任务,非常简单。 :-)


3
你的Intel i5有两个核心,超线程技术让它们看起来像是四个核心。所以你只能得到四个核心的并行处理能力,其余的线程被时间分片了。
假设每个线程都需要1MB的RAM用于线程创建,然后再加上每个线程处理文件所需的内存。这样可以让你明白为什么会出现内存不足的错误。你处理的文件有多大?如果文件非常大,在同一时间内将它们全部放入内存中就会遇到问题。
我假设接收文件的服务器可以接受多个连接,因此尝试使用多个线程是有价值的。
我建议首先使用一个线程进行基准测试,然后增加线程数量,直到性能曲线趋于平缓。

你说得对,服务器可以同时执行至少三个命令(甚至可能是36个)。所以我想同时在线程1、2上运行两个命令,然后在主线程上运行第三个命令? - user615927

1

将线程数量调整到机器大小的一种方法是使用

int processors = Runtime.getRuntime().availableProcessors();
int threads = processors * N; // N could 1, 2 or more depending on what you are doing.
ExecutorService es = Executors.newFixedThreadPool(threads);

1

使用比机器核心数更多的线程会减慢整个过程的速度。只有达到这个数量之后,才会加速。


如果我的处理器有4个核心,我相信在同一时间使用3个线程速度最优?至少我已经通过使用多个命令检查了这种方式的时间:Executors.fixedThreadPool(2) - 耗时6:40分钟,fixedThreadPool(3) - 耗时5:28分钟。ThreadPool(4) - 耗时9:56分钟,时间更长!ThreadPool(8) - 耗时13:13分钟。总之,我能够使用的线程的最大数量是3?! - user615927

1

暴力破解:逐步调整配置文件。逐渐增加线程数并检查性能。由于连接数仅为36,因此应该很容易。


1

你需要明白的是,如果你创建了36个线程,但你仍然只有一到两个处理器,这将会大部分时间在不同的线程之间进行切换。

我建议你先把线程数适当增加一些,比如说6个,看看它们的运行情况,然后再继续调整。


1

首先你必须找出瓶颈在哪里

  • 如果是SSH连接,通常并行打开多个连接没有帮助。最好使用一个连接上的多个通道(如果需要)。

  • 如果是磁盘IO,创建多个线程写入(或读取)只有在它们访问不同磁盘时才有帮助(这种情况很少见)。但是,当您在一个线程中等待磁盘IO时,您可以有另一个线程执行CPU密集型任务。

  • 如果是CPU,并且您有足够的空闲核心,则更多的线程可以提高性能。如果它们不需要访问共享数据,甚至可以更多。但是,比核心数多的线程(+一些执行IO的线程)并没有帮助。(还要考虑通常还有其他进程在服务器上运行。)


另外注意:在类Unix系统中,htop是一个很好的工具,可以查看CPU和内存使用情况。 - trillions

0

请确保您不要创建比处理单元更多的线程,否则您可能会因上下文切换而产生更多的开销,而不是获得并发性所带来的好处。同时,请记住您只有1个硬盘和1个硬盘控制器,因此我怀疑多线程在这里并没有什么帮助。


请记住,由于您只有1个硬盘和1个硬盘控制器,因此我怀疑多线程在这里根本没有帮助。为什么?如果我可以通过Putty打开5个不同的窗口(连接到服务器的5个连接)并执行5个命令组,那么我应该能够使用Java做同样的事情吧?! - user615927
不清楚您是在一个系统上创建36个文件还是在多个系统上创建,如果您正在向单个系统进行36个连接,则驱动并发可能会成为问题,除非该系统正在运行超出单个HDD的存储系统。您必须想象HDD是一个单线程实体,一次只能进行一次写入。 - Lazarus
你需要明白,在线程之间切换并不是“免费”的,它会花费时间。虽然这只是一点点的时间,但当你将这个时间乘以36个线程,这些线程可能每秒钟切换上下文多次,并且库可能没有针对多线程进行重度优化时,你会看到比你想象中更多的时间。线程不是万能药,除非你真正理解了其中的原理,否则我建议不要使用它,就像一个非常锋利的刀子。如果你不知道如何正确使用它,那么你会伤害自己。 - Lazarus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接