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

时间:2017-01-18 16:20:52

标签: java multithreading command-line deadlock swingworker

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

最小的工作示例(没有任何有用的东西)如下:

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();
    }
}
}

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

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

    tester.run();

观察到的行为:输出包括Starting 1 \ n [...] Starting 1034 \ n之后,该过程仍处于活动状态,但不再打印任何行。在发生死锁时,我的进程的线程数为31014。该实验在24芯机器上进行。

预期的行为:进程应该继续打印k = n for k = 1,2,...永远或者因创建的ThreadPools太多而引发OutOfMemoryError

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

1 个答案:

答案 0 :(得分:0)

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

程序执行了k轮(即打印Starting k\n)后,创建了大小为34的k个ThreadPools。除此之外,除了上一代执行doInBackground()方法的30个以外的所有线程最后都没有执行任何代码,但仍然是running。仍然执行代码的30个线程在get()put()方法上同步并陷入死锁,因为AWT-eventdispatch线程由于某种原因不执行publish()方法。这种僵局是由于创建了很多线程并且running(尽管大多数线程不活跃且不参与竞赛)这一事实造成的。

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