Параллелизм JDK 1.6 Java: Активное ожидание добивается большего успеха, чем передача сигналов? Эффективный Java № 51

"Эффективный Java Joshua Bloch", Объект 51 не о в зависимости от планировщика потока, а также не остающихся потоков излишне в выполнимом состоянии. Заключенный в кавычки текст:

Основная техника для подавления количества выполнимых потоков состоит в том, чтобы иметь каждый поток, делают небольшой объем работы и затем ожидают некоторого условия с помощью Object.wait или в течение некоторого времени протекать с помощью Thread.sleep. Потоки не должны активное ожидание, неоднократно проверяя ожидающую, что что-то произойдет структуру данных. Помимо создания программы, уязвимой для капризов планировщика, активное ожидание может значительно увеличить нагрузку на процессор, уменьшив объем полезной работы, которую другие процессы могут выполнить на той же машине.

И затем продолжает показывать микросравнительный тест активного ожидания по сравнению с использованием сигналов правильно. В книге активное ожидание делает 17 раундов trips/s, тогда как ожидать/уведомлять версия делает 23 000 распространений в прямом и обратном направлениях в секунду.

Однако то, когда я попробовал тот же сравнительный тест на JDK 1.6, я вижу совсем противоположное - активное ожидание делает 760K распространения в прямом и обратном направлениях/секунда, тогда как ожидать/уведомлять версия делает 53.3K roundtrips/s - то есть, ожидает/уведомляет, должно было быть в ~1400 раз быстрее, но оказывается в ~13 раз медленнее?

Я понимаю, что активные ожидания не хороши, и передача сигналов еще лучше - загрузка ЦП составляет ~50% на версии активного ожидания, тогда как это остается в ~30% на ожидать/уведомлять версии - но является там чем-то, что объясняет числа?

Если это помогает, я выполняю JDK1.6 (32 бита) в (Core i5) Win 7 x64.

ОБНОВЛЕНИЕ: Источник ниже. Для выполнения занятых инструментальных средств изменитесь, базовый класс PingPongQueue к BusyWorkQueue импортируют java.util. LinkedList; импорт java.util. Список;

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();
    }
}
6
задан Raghu 22 July 2010 в 17:50
поделиться