【问题标题】:ThreadPoolExecutor runs only first runnableThreadPoolExecutor 只运行第一个可运行对象
【发布时间】:2015-05-27 12:16:35
【问题描述】:

我有一个线程池执行器

private ExecutorService exec=Executors.newCachedThreadPool();

我有一堂课

class MyCallable implements Callable<String>{

    private ReentrantLock lock=new ReentrantLock();

    private Condition cond=lock.newCondition();


    public String call(){

    System.out.println("TEST!!);
    try{
      lock.lock();
      cond.await();
    }finally{
       lock.unlock();
    }
   }
}

我愿意:

for(int i=0;i<10000;i++){
   exec.submit(new MyCallable());
}

我想拥有 10000 个等待调用的线程,但我只能看到一个 TEST!在我的日志中,所以它只提交一次我的任务,为什么它会冻结???我的可调用对象在每个对象实例中都有自己的锁,如何有 1000 个等待线程?

【问题讨论】:

  • for(...) 循环结束后,您还要等待多长时间才能终止程序?
  • 谢谢,但我不能使用固定线程池,我不知道可调用的数量,10k 只是一个例子@Hanno,我只有这个循环,我什至不使用未来。获取()
  • 会不会是你的程序在许多新线程执行之前就终止了?如果你只是在循环之后 Thread.sleep(1000) 会发生什么?
  • 无论如何,您都不需要知道可调用对象的数量。 FixedThreadPool 一次只执行 n 个线程。

标签: java multithreading executorservice


【解决方案1】:

我获取了您的代码,填写了缺失的位,修复了编译错误并运行了它。它打印“测试!!”很多次。如果我将任务数量减少到合理的数量,它就会运行完成。如果我将其设置为 10,000,则测试将失败,并显示 OOME 表示无法分配更多线程堆栈。


我想拥有 10000 个等待调用的线程

我的测试表明,许多线程会导致您的内存不足。这真的,真的不是一个好主意。


这是我使用的确切代码:

package test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorTest {

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 10000; i++) {
            exec.submit(new MyCallable());
        }
    }
}


package test;

import java.util.concurrent.Callable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class MyCallable implements Callable<String> {

    private ReentrantLock lock = new ReentrantLock();

    private Condition cond = lock.newCondition();

    public String call() {

        System.out.println("TEST!!");
        try {
            lock.lock();
            cond.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return "foo";
    }
}

这是while(true) {await} 测试:

[steve@newbox ~]$ cat ./workspace/test/src/test/CondAwait.java 
package test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class CondAwait {

    public static void main(String[] args) throws InterruptedException {
         ReentrantLock lock = new ReentrantLock();

         Condition cond = lock.newCondition();
         lock.lock();
         while(true){
             System.out.println("Hi!");
             cond.await();
         }
    }

}
[steve@newbox ~]$ java -cp ./workspace/test/bin test.CondAwait 
Hi!
^C[steve@newbox ~]$ 

简而言之,主线程在第一个await 调用中冻结...正如我所预料的那样。


我同意这不是一个好主意,但是那些线程就像一个请求缓存,用户发送一个请求,它的处理器应该被放入线程池中,如果我们每秒有 10k 个请求,你会看到池中有 10k 个线程。请建议如何做得更好。

使用有界线程池,并将边界设置为大致等于您拥有的核心数。

当然,您不会同时暂停 10,000 个可调用对象,但这是一件好事。

如果这些可调用对象旨在模拟等待几秒钟以等待外部事物响应的请求,那么最多 100 个线程池可能是合理的。但是,如果您真的非常需要大规模并行性,那么您应该考虑使用 NIO 选择器,这样少量工作线程就可以运行大量请求,将它们交错而不是阻塞在 I/O 中。

【讨论】:

  • 谢谢,真奇怪,请尝试运行 while(true){cond.await();} 谢谢
  • 谢谢,我同意这不是一个好主意,但是那些线程就像一个请求缓存,用户发送一个请求,它的处理器应该被放入线程池,如果我们有每秒 10k 个请求,您将在池中看到 10k 个线程。请建议如何做得更好。谢谢
  • 谢谢,但是如果我之前调用 lock.lock() 为什么我需要持有锁,这并不能解释为什么我在等待之前看不到语句。
  • 而且我还注意到我未处理的消息在tomcat关闭之前开始处理。奇怪:/
  • 谢谢,是的,这可能是个好主意,但为什么我不能同时运行 100 个线程,突然感觉外部服务中断了,而它们 100 会卡在空中。不好吗?当然我们会有future.get(10,TimeUnit.SECONDS),但我什至不能启动多个无限循环运行的线程。
【解决方案2】:

查看java.util.concurrent.CyclicBarrier 课程。正如其 JavaDoc 中所述:

允许一组线程全部等待的同步辅助 彼此到达共同的障碍点。

你的每个线程都需要调用屏障的方法await。这意味着线程被挂起,直到 Barrier 达到预期的线程数或发生超时。

【讨论】:

    【解决方案3】:

    我刚试过你的程序,它对我来说很好用。我可以看到多个任务正在运行。

    public class MyCallable implements Callable<String> {
    
        private ReentrantLock lock = new ReentrantLock();
        private Condition cond = lock.newCondition();
    
        public String call() {
            System.out.println("TEST!!");
            try {
               lock.lock();
               cond.await();
            } catch (InterruptedException e) {
               e.printStackTrace();
            } finally {
               lock.unlock();
            }
            return "done";
        }
    
        public static void main(String[] args) {
            ExecutorService service = Executors.newCachedThreadPool();
            for (int i = 0; i < 10; i++) {
               service.submit(new MyCallable());
            }
        }
    }
    

    我建议 10000 个并发任务不是一个好主意。你有多少个处理器? ;)

    【讨论】:

    • 谢谢,对我来说没关系,因为我需要将等待rabbitmq响应的线程存储在某个地方,我该怎么做?我不知道。
    猜你喜欢
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    • 2020-01-23
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    • 2016-12-27
    • 2022-01-17
    相关资源
    最近更新 更多