Подтвердить что ты не робот

Какая очередь блокировки Java наиболее эффективна для однопользовательских однопользовательских сценариев

Я работаю над стандартной системой Java с критическими требованиями к времени для моих производителей (1/100 мс вопросов).

У меня есть продюсер, размещающий материал в блокирующей очереди, и один потребитель позже подбирает этот материал и сбрасывает его в файл. Потребитель блокирует, когда данные недоступны.

Очевидно, что блокировка очереди - это соответствующий интерфейс, но какую фактическую реализацию я должен выбрать, если я хочу минимизировать затраты для производителя? Я хотел играть как можно меньше на таких вещах, как блокировка и распределение, когда я помещаю вещи в очередь, и я не против, если потребитель должен ждать намного дольше или работать намного сложнее.

Есть ли реализация, которая может быть быстрее, потому что у меня есть только один потребитель и единственный производитель?

4b9b3361

Ответ 1

Ну, на самом деле не так много вариантов. Позвольте мне пройти через перечисленные подклассы:

DelayQueue, LinkedBlockingDeque, PriorityBlockingQueue, и SynchronousQueue сделаны для специальных случаи, требующие дополнительной функциональности; они не имеют смысла в этом сценарии.

Это оставляет только ArrayBlockingQueue и LinkedBlockingQueue. Если вы знаете, как определить, нужен ли вам ArrayList или LinkedList, вы можете, вероятно, ответить на этот вопрос самостоятельно.

Обратите внимание, что в LinkedBlockingQueue "связанные узлы динамически создаются при каждой вставке"; это может склонить вас к ArrayBlockingQueue.

Ответ 2

Вы можете написать чувствительную к задержкам систему на Java, но вы должны быть осторожны с распределением памяти, информацией, передаваемой между потоками и блокировкой. Вы должны написать несколько простых тестов, чтобы узнать, сколько времени это стоит на вашем сервере. (Они меняют основы на ОС и архитектуру памяти)

Если вы это сделаете, вы можете получить стабильные тайминги для операций до 99,99% времени. Это не правильно в режиме реального времени, но может быть достаточно близко. В торговой системе использование Java вместо затрат на C может стоить 100 фунтов стерлингов, однако стоимость разработки на C/С++, а не Java, вероятно, будет намного выше. например с точки зрения гибкости, которую он дает нам, и количества ошибок, которые он сохраняет.

Вы можете получить достаточно близкое количество джиттера, которое вы увидите в программе на C, выполняющей то же самое. Некоторые называют эту "C-like" Java.

К сожалению, для передачи объекта между потоками в Java через ArrayBlockingQueue на серверах, на которых я работаю, требуется около 8 микросекунд, и я предлагаю вам протестировать это на своих серверах. Простой тест - передать System.nanoTime() между потоками и посмотреть, сколько времени потребуется.

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

Ответ 3

LinkedBlockingQueue будет иметь стоимость установки O (1), если не будет задержки выделения памяти. Очень большой ArrayBlockingQueue будет иметь стоимость вложений O (1), если система не заблокирована из-за сбора мусора; он, однако, блокируется при вставке, когда он имеет емкость.

Даже при одновременной сборке мусора я не уверен, что вы должны писать систему реального времени на управляемом языке.

Ответ 4

Если требования по синхронизации настолько плотные, вам, вероятно, потребуется провести тщательный бенчмаркинг на точно таком же аппаратном обеспечении, чтобы определить наилучшее соответствие.

Если бы я мог догадаться, я бы пошел с ArrayBlockingQueue, потому что массивные коллекции имеют тенденцию иметь хорошую локальность ссылок.

Ответ 5

Я согласен с замечанием Эндрю Даффи, что внедрение такой ограниченной во времени системы в java может быть ошибкой. Независимо от того, что, если вы состоите в браке с JVM, и вы не ожидаете, что эта очередь будет работать полностью (то есть потребитель может справиться с нагрузкой), я думаю, вам лучше всего помочь пользовательская реализация, аналогичная ArrayBlockingQueue но урезано/оптимизировано для одного сценария производителя/потребителя. В частности, мне нравится идея вращения на стороне производителя, чтобы ждать пространства, а не блокировки.

Я ссылался на java.concurrent источники для руководства и разработал этот алгоритм...

Это выглядит очень хорошо для меня, но он полностью непроверен и может быть не быстрее на практике (возможно, не революционный, но я сам его проверял:-). Мне все равно понравилось... Вы можете найти недостаток?

псевдокод:

private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final E[] buffer;
private int head = 0
private int tail = 0;

public void put(E e) {
  if (e == null) throw NullPointerException();

  while (buffer[tail] != null) {
    // spin, wait for space...  hurry up, consumer!
    // open Q: would a tight/empty loop be superior here?
    Thread.sleep(1);
  }

  buffer[tail] = e;
  tail = (tail + 1) % buffer.length;

  if (count.getAndIncrement() == 0)  {
    sync(takeLock) { // this is pseudocode -- should be lock/try/finally/unlock
      notEmpty.signal();
    }
  }
}

public E take() {
  sync(takeLock) {
    while (count.get() == 0) notEmpty.await();
  }
  E item = buffer[head];
  buffer[head] = null;
  count.decrement();
  head = (head + 1) % buffer.length;

  return item;
}

Ответ 6

ArrayBlockingQueue, вероятно, будет быстрее, поскольку вам не нужно создавать узлы ссылок при вставке. Обратите внимание, однако, что ArrayBlockingQueue имеет фиксированную длину, если вы хотите, чтобы ваша очередь становилась сколь угодно большой (по крайней мере, Integer.MAX_INT), вам придется использовать LinkedBlockingQueue (если вы не хотите выделять массив элементов Integer.MAX_INT).