Я наблюдал странное поведение приложения командной строки, которое вызывает SwingWorkers. Код не оптимален в том смысле, что он создает много пулов потоков. Однако из-за управления переменной generation
все эти пулы, кроме последнего, не выполняют никакого кода. Это означает, что потоки из этих бассейнов не только не участвуют в гонках для замков, но также должны собираться и исчезать мусор.SwingWorker.process() не получает вызов в приложении командной строки
Минимальный рабочий пример (не делает ничего полезного) заключается в следующем:
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();
}
}
}
Затем этот класс работает в главном методе моей программы по:
Tester tester = new Tester(30 * 400, 30);
tester.run();
Наблюдаемое поведение : Выход состоит из запуска 1 \ n [...] Начиная с 1034 \ n После этого процесс все еще жив, но больше строк не печатается. Количество потоков для моего процесса - 31014 в момент блокировки. Эксперимент проводили на машине с 24 сердечниками.
Ожидаемое поведение: Процесс должен поддерживать печать Запуск К \ п к = 1, 2, ... навсегда или бросить OutOfMemoryError
вызванное слишком много ThreadPools созданных.
Представленный пример имеет ограниченную отладку. В какой-то момент у меня появилось больше команд printf, они подразумевали, что тупик возникает, когда все созданные потоки текущего поколения вызвали свои методы publish()
, но метод process()
не вызывается EDT.
Что вы ожидаете? Когда 'this.done == this.size', вы вызываете' run() ', который устанавливает' done' в ноль. Сообщение печатается только в том случае, когда 'this.done> this.size', но этого достичь невозможно, когда вы устанавливаете' done' в ноль до достижения этого условия. Я не уверен, поняли ли вы цели пулов потоков, когда вы неоднократно создаете новые пулы ... Но цель этого странного кода в любом случае неясна. – Holger
Вы печатаете сообщение «Starting ...» непосредственно перед созданием нового пула потоков, поэтому каждый раз, когда вы видите это сообщение, вы создаете новый пул потоков с фиксированным числом из 15 потоков. Поэтому, когда вы видите сообщение «Начиная с 2813», вы создали потоки 2813 × 15 == 42195. И вы думаете, что вы действительно можете исключить отношения между вашей программой, зависающими и создав потоки 42195, все из которых неоднократно вызывают методы «synchronized» на одном и том же объекте? – Holger
Извините, если это звучит слишком жестко, но минимальное требование для вопроса - описать ожидаемое поведение, и невозможно угадать намерение вашей программы, поскольку оно просто не имеет никакого смысла. Все потоки изменяют одни и те же переменные, поэтому, если поток завершен или нет, он является чисто случайным. И так как каждое завершение запускает начало еще 15 потоков, вы просто создали вариант печально известной вилочной бомбы. – Holger