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 停止运行。
我观察到调用 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 停止运行。