java - ExecutorService 的奇怪行为

标签 java multithreading concurrency java.util.concurrent

我有 5000 个类似的 Callable 任务要在 Executors.newFixedThreadPool(8) 创建的 ExecutorService 的 8 个线程中执行。每个任务都会进入数据库来检索大量数据进行处理。

99% 的时间一切正常,但有时我会在日志文件中看到非常奇怪的执行日志消息,当数据库缓慢或卡住(不要问为什么)并且 8 个当前正在运行的任务被停滞且尚未完成时全部8个线程,ExecutorService开始提交更多任务来一一执行!

所以日志显示,在某个时刻,ExecutorService 变得疯狂,开始调用等待队列中越来越多任务的 Callable 的 call() 方法,而不等待前面的任务完成。越来越多的任务向 DB 发送请求,最终导致 DB 崩溃,Java 堆内存耗尽。

ExecutorService 内部似乎发生了一些奇怪的事情,或者我对情况的理解是错误的。有人见过类似的东西吗?

我的脑子已经满了

附:这是来自 Java API 的引用:

Executors.newFixedThreadPool(int nThreads)

Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.

这是否真的会发生,我的任务导致线程死亡,ExecutorService 创建更多线程并向它们提交新的 8 个任务,然后它们死亡,ExecutorService 创建另外 8 个线程并提交更多 8 个任务?

p.s.s.:Callable 的 call() 内的整个操作都被 try catch 包围,因此如果我的操作内发生任何异常,异常将被捕获并记录。这一切都没有发生。该调用被调用并且永远不会返回,而下一个任务被一一调用并且永远不会返回,永远不会完成,也永远不会抛出任何异常。

我怀疑我的任务导致线程池中的线程死亡。怎么可能模仿?

最佳答案

我也会尝试猜测:

  1. 您提交了 5000 个涉及从数据库获取数据的任务。
  2. 不久之后,您会在所需的行/表上遇到严重的锁争用。也许外部进程正在获取用于写入的独占锁。也许出现了僵局。
  3. 任务一个接一个地阻塞,等待共享/读锁被授予。
  4. 看起来好像所有 8 个线程都已挂起,等待 I/O
  5. 不久之后,数据库/数据库驱动程序注意到任务等待共享锁的时间太长。它概括地按顺序向任务分发Lock Wait Timeout 异常。
  6. 因此,任务一个接一个地从队列中失败,等待的任务被插入执行,但又再次失败。

请注意,任务中的异常不会停止 ExecutorService。它只会将该任务标记为已完成并继续。

请参阅此示例:

public class Foo {

    static class Task implements Callable<String> {
        private static AtomicInteger i = new AtomicInteger(1);

        public String call() throws Exception {
            i.incrementAndGet();
            if (i.get() % 2 != 0) {
                throw new RuntimeException("That's odd, I failed.");
            }
            return "I'm done";
        }
    }

    public static void main(String[] args) throws Exception {
        ExecutorService es = Executors.newFixedThreadPool(2);
        List<Future<String>> futures = new ArrayList<Future<String>>();
        for (int i = 0; i < 5; i++) {
            futures.add(es.submit(new Task()));
        }
        for (Future<String> future : futures) {
            try {
                System.out.println(future.get());
            } catch (ExecutionException ee) {
                System.err.println(ee.getCause());
            }
        }
        es.shutdown();
    }
}

可能的输出:

I'm done
I'm done
I'm done
java.lang.RuntimeException: That's odd, I failed.
java.lang.RuntimeException: That's odd, I failed.

关于java - ExecutorService 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7990281/

相关文章:

java - MYSQL 一类一个连接

algorithm - 在分布式系统中复制文件,以便所有服务器都有所有文件的副本

java - 使用本地对象引用如何保证线程安全?

等待 goroutine,但同时做一些事情

django - 使用 PostgreSQL 在 Django 中获取下一个工作项

java - 格式化包含非 ASCII 字符串的 Protobuf 消息

java - MapStruct:根据( boolean )值排除属性

java - 如何将spring MVC jsp页面绑定(bind)到两个类

c++ - Boost线程、Posix线程和STD线程,为什么它们提供不同的性能?

C# 在新线程或任务中打开表单?