«Эффективная Java» Джошуа Блоха, пункт 51, не касается зависимости от планировщика потоков, а также не держит потоки без необходимости в состоянии выполнения. Цитируемый текст:
Основная техника для уменьшения количества работающих потоков - это иметь каждый поток
сделать небольшой объем работы, а затем ждать какого-то условия с помощью Object.wait или для некоторых
время истекло, используя Thread.sleep. Потоки не должны быть заняты - ждать, повторно проверяя данные
структура ждет чего-то, чтобы случиться. Помимо того, что делает программу уязвимой для
капризы планировщика, занятое ожидание может значительно увеличить нагрузку на процессор,
сокращение объема полезной работы, которую другие процессы могут выполнять на той же машине.
А затем показывает микробенчм занятого ожидания и правильного использования сигналов. В книге ожидание «занято» выполняет 17 рейсов в секунду, а версия «ожидание / уведомление» - 23 000 рейсов в секунду.
Однако, когда я попробовал тот же самый тест на JDK 1.6, я вижу наоборот: занятое ожидание делает 760K циклов / секунду, тогда как версия ожидания / уведомления делает 53,3K циклов / с - то есть ожидание / уведомление был в ~ 1400 раз быстрее, но оказывается в ~ 13 раз медленнее?
Я понимаю, что ожидание занятости не является хорошим, а сигнализация все еще лучше - загрузка процессора составляет ~ 50% в версии ожидания занятости, тогда как в версии ожидания / уведомления она остается на уровне ~ 30% - но есть ли что-то, что объясняет цифры?
Если это поможет, я использую JDK1.6 (32-разрядную версию) на Win 7 x64 (core i5).
ОБНОВЛЕНИЕ : источник ниже. Чтобы запустить занятую рабочую среду, измените базовый класс PingPongQueue на BusyWorkQueue
import java.util.LinkedList;
import java.util.List;
abstract class SignalWorkQueue {
private final List queue = new LinkedList();
private boolean stopped = false;
protected SignalWorkQueue() { new WorkerThread().start(); }
public final void enqueue(Object workItem) {
synchronized (queue) {
queue.add(workItem);
queue.notify();
}
}
public final void stop() {
synchronized (queue) {
stopped = true;
queue.notify();
}
}
protected abstract void processItem(Object workItem)
throws InterruptedException;
private class WorkerThread extends Thread {
public void run() {
while (true) { // Main loop
Object workItem = null;
synchronized (queue) {
try {
while (queue.isEmpty() && !stopped)
queue.wait();
} catch (InterruptedException e) {
return;
}
if (stopped)
return;
workItem = queue.remove(0);
}
try {
processItem(workItem); // No lock held
} catch (InterruptedException e) {
return;
}
}
}
}
}
// HORRIBLE PROGRAM - uses busy-wait instead of Object.wait!
abstract class BusyWorkQueue {
private final List queue = new LinkedList();
private boolean stopped = false;
protected BusyWorkQueue() {
new WorkerThread().start();
}
public final void enqueue(Object workItem) {
synchronized (queue) {
queue.add(workItem);
}
}
public final void stop() {
synchronized (queue) {
stopped = true;
}
}
protected abstract void processItem(Object workItem)
throws InterruptedException;
private class WorkerThread extends Thread {
public void run() {
final Object QUEUE_IS_EMPTY = new Object();
while (true) { // Main loop
Object workItem = QUEUE_IS_EMPTY;
synchronized (queue) {
if (stopped)
return;
if (!queue.isEmpty())
workItem = queue.remove(0);
}
if (workItem != QUEUE_IS_EMPTY) {
try {
processItem(workItem);
} catch (InterruptedException e) {
return;
}
}
}
}
}
}
class PingPongQueue extends SignalWorkQueue {
volatile int count = 0;
protected void processItem(final Object sender) {
count++;
SignalWorkQueue recipient = (SignalWorkQueue) sender;
recipient.enqueue(this);
}
}
public class WaitQueuePerf {
public static void main(String[] args) {
PingPongQueue q1 = new PingPongQueue();
PingPongQueue q2 = new PingPongQueue();
q1.enqueue(q2); // Kick-start the system
// Give the system 10 seconds to warm up
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
// Measure the number of round trips in 10 seconds
int count = q1.count;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
System.out.println(q1.count - count);
q1.stop();
q2.stop();
}
}