В чем причина реализации ArrayBlockingQueue с одним замком на хвосте и голове? - PullRequest
0 голосов
/ 07 июня 2018

Я читаю Java Concurrency на практике, сейчас я нахожусь на странице о сравнении LinkedBlockingQueue с ArrayBlockingQueue, и я нашел этот текст

Этот тест предполагает, что LinkedBlockingQueue масштабируется лучше, чем ArrayBlockingQueue.Это можетсначала кажется странным: связанная очередь должна выделять объект узла ссылки для каждой вставки, и, следовательно, кажется, выполняет больше работы, чем основанная на массиве очередь.Тем не менее, даже несмотря на то, что он имеет больше распределения и накладных расходов GC, связанная очередь обеспечивает более параллельный доступ путём и взятием, чем очередь на основе массива, потому что лучшие алгоритмы связанной очереди позволяют обновлять голову и хвост независимо друг от друга.Поскольку распределение обычно локально, потоки, алгоритмы, которые могут уменьшить конкуренцию за счет большего выделения, обычно масштабируются лучше (это еще один случай, когда интуиция, основанная на традиционной настройке производительности, противоречит тому, что необходимо для масштабируемости).

с таким изображением

enter image description here

Я погрузился в исходный код ArrayBlockingQueue и, действительно, нашел единственную блокировку для put() и take()операции (как факт, по смыслу, я нашел только синхронизацию для доступа к полю счетчика, где есть информация о размере текущего контента в очереди).

код метода put

public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

Мой вопрос: что мешает реализовать ArrayBlockingQueue с двумя блокировками вместо одной блокировки для хвоста и головы.Я могу представить себе некоторый алгоритм, который проверяет, есть ли место впереди, чтобы переместить место следующего дубля и место следующего пута;

put() проверка на равенство с местом, где take() равно и равно ли оно put() ожидает, и то же самое сделано take().Кажется, это будет работать, потому что put() и take() в некотором смысле имеют постоянное направление движения вдоль массива.Конечно, мы должны принимать во внимание начальное состояние с помощью некоторого логического флага.

Конечно, очевидно, что в настоящее время ArrayBlockingQueue имеет более низкую пропускную способность, чем LinkedBlockingQueue, из-за большего количества конфликтов и, следовательно, более сериализованного кода (Закон Амдала).Но что может быть проблемой, если написать его с двумя блокировками, а также с меньшим временем выделения!

извините за мой плохой английский, заранее!

...