SwingWorker.process() 未在命令行应用程序中调用

SwingWorker.process() not getting called in a command line application

我观察到调用 SwingWorkers 的命令行应用程序有一个奇怪的行为。该代码在创建大量线程池的意义上不是最佳的。但是,由于generation变量的控制,除了最后一个之外,所有这些pools都不执行任何代码。这意味着来自这些池的线程不仅不参与锁的竞争,而且还应该被垃圾收集并消失。

一个最小的工作示例(没有任何用处)如下:

package test;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.SwingWorker;

public class Tester {

private final int threads;
private ExecutorService threadPool;
private final int size;
private long currGen;
private int left;
private int done;

public Tester(int size, int threads) {
    this.threads = threads;

    this.size = size;

    this.currGen = 0;

    this.left = size;
    this.done = 0;
}

private class TestWorker extends SwingWorker<Object, Object> {
    private final Tester tester;
    private final long generation;

    TestWorker(Tester tester, long generation) {
        this.tester = tester;
        this.generation = generation;
    }

    @Override
    protected Object doInBackground() throws Exception {
        while(this.tester.get(generation)) {
            Thread.sleep(1);
            publish(1);
        }

        return null;
    }

    @Override
    protected void process(List<Object> results) {
        for(Object n : results) {
            this.tester.put(generation);
        }
    }
}

public void run() {      
    this.currGen++;        
    this.left = size;
    this.done = 0;

    System.out.printf("Starting %d\n", currGen);

    this.threadPool = Executors.newFixedThreadPool(threads + 4);

    for (int threadId = 0; threadId < threads; threadId++) {
        threadPool.submit(new TestWorker(this, currGen));
    }
}

public synchronized boolean get(long generation) {        
    if (generation != currGen) {
        return false;
    }

    if (this.left == 0) {
        return false;
    }

    this.left--;

    return true;
}

public synchronized void put(long generation) {           
    if (generation != currGen) {
        return;
    }

    this.done++;

    if (this.done == this.size) {
        this.run();
    }
}
}

然后,这个class在我的程序的main方法中是运行:

    Tester tester = new Tester(30 * 400, 30);

    tester.run();

观察到的行为: 输出包括 Starting 1\n [...] Starting 1034\n After that the process is still alive, but no more打印线条。在死锁时刻,我的进程的线程数是 31014。实验是在24核机器上进行的

预期行为: 进程应继续打印 Starting k\n for k = 1, 2, ... 永远或抛出 OutOfMemoryError创建的线程池过多。

提供的示例调试有限。在某些时候我有更多的 printf 命令,它们暗示当当前一代的所有创建的线程都调用了它们的 publish() 方法但 EDT 未调用 process() 方法时发生死锁。

经过与 Holger 的讨论,问题似乎是由创建多个 ThreadPool 引起的:

程序执行 k 轮后(即打印 Starting k\n),创建了 k 个大小为 34 的线程池。其中,除了最后一代执行doInBackground()方法的30个线程外,所有线程都没有执行任何代码,但仍然是running。仍在执行代码的 30 个线程在 get()put() 方法上同步并陷入死锁,因为 AWT-eventdispatch 线程由于某种原因没有执行 publish() 方法。这个死锁的唯一原因是创建了很多线程并且 running(即使它们中的大多数都不活跃并且不参与竞争)。

讨论后的共识似乎是:由于创建了太多线程,系统(Java 外部)违反了某些约束,导致 JVM 停止运行。