Проблема в реализации потоков в виртуальной машине. В спецификации не указано, как это сделать. Предполагается, что потоки Java сопоставляются с собственными потоками Windows, а затем используют планировщик Windows для совместного использования временных интервалов. Неясно, действительно ли это происходит, и вся официальная документация поддерживает только информацию о потоках, запущенную в Solaris.
Я считаю, что основная проблема заключается в деталях реализации для вытеснения потоков. Вероятно, это вызвано некоторой комбинацией оптимизации кода при компиляции и управлении вытеснением между JVM и собственной ОС. JVM могут использовать вызовы методов в качестве точек для вытеснения потоков, и я думаю, что частью проблемы здесь являются два цикла, которые вы вызываете один поверх другого. Если вы разберетесь с ними с помощью вызова функции, он будет работать намного лучше на моей машине. Я использую 1.6.0_23 64-битную серверную виртуальную машину в Windows 7.
SwingWorker<Void, Integer> testTask = new SwingWorker<Void, Integer>() {
private int k;
private void inc() {
this.k++;
}
private void innerLoop(int i) {
for (int j=0; j<100000; j++) {
if (i==j && i%10000 == 0)
this.inc();
}
}
@Override
protected Void doInBackground()
throws Exception {
System.out.println("Started");
for (int i=0; i<200000; i++) {
this.innerLoop(i);
}
System.out.println(k+" "+(System.currentTimeMillis()-start));
return null;
}
};
Однако даже после запуска нескольких из них возникают проблемы. Лучшее решение - добавить вызов к Thread.yield()
каждый раз, когда вы запускаете внутренний цикл. Это гарантирует, что скомпилированный код дает планировщику возможность каждой итерации выгружать поток.