2009-12-30 3 views
3

Предположим, что у меня есть метод Java, как:Предварительное создание объектов в отдельном потоке

public Ouput respond(Input input) { /* .. */ } 

Объект Output имеет много полей, - некоторые из этих полей зависят от Input объекта, а остальные предопределены. Я хочу сделать поток, который вызывает respond(), чтобы вернуться как можно быстрее.

Для этого я хочу, чтобы породить еще один поток, который предварительно-создает Output объект, устанавливает некоторые из полей и помещает их в очередь, так что нить работает respond() может забрать его из очереди, установите остальные поля и возврат Это.

Каков наилучший способ реализовать что-то подобное? Мой прототип использует ограниченный LinkedBlockingQueue, но есть ли лучшие способы реализации этой идеи?

Моя цель - как можно быстрее получить метод respond(), поэтому другие предложения, которые соответствуют этой цели, также приветствуются. :-)

+1

Чтобы помочь вам оптимизировать метод, нам нужно будет увидеть код класса 'Output'; важно знать, о каких областях мы говорим. (Я говорю это, потому что кажется, что построение экземпляра 'Output' со значениями по умолчанию для кучи его полей не является столь трудоемким процессом ...) – delfuego

+0

@delfuego Я составил код выше; фактический код более сложный, но мои тесты/профилирование показывают, что накладные расходы на строительство значительны. Класс 'Output' находится в третьей стороне lib, у источника, к которой у меня нет доступа. –

+0

Почему связанный, а не массив? Я предположил бы (возможно наивно), что ArrayBlockingQueue работает немного лучше, чем LinkedBlockingQueue только потому, что реализация массива не имеет дополнительных накладных расходов на поддержание объектов ссылки. – Suppressingfire

ответ

2

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

Конечно, это может работать, только если вы можете контролировать весь процесс и можете изменить вызывающего абонента, который получает результат ответа().

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

+0

@ Kosi2801 К сожалению, у меня нет контроля над жизненным циклом созданного объекта, поэтому объединение объектов может быть нелегко реализовать в моем случае. Тем не менее, это то, о чем я должен подумать - спасибо за то, что он поднял это! –

3

Это звучит как случай, когда вы можете хорошо использовать Future и Executor. Подробнее здесь:

Вот конкретный пример:

ExecutorService executor = Executors.newSingleThreadExecutor(); 

final Input input = ... 
Future<Output> future = executor.submit(new Callable<Output>() { 
    public Output call() { 
    return respond(input); 
    } 
}); 

// do some work 

Output output = future.get(); // this blocks until it's done 

В вашем случае, так как вы упомянули, что некоторые из полей в Output объекта являются для немедленного использования, в то время как другие используются позднее, вы можете структурировать свой объект Output содержат фьючерсы в качестве полей. Например:

public class Output { 
    private String fieldA; // immediate field 
    private Future<String> fieldB; // delayed field 

    public void setFieldA(String fieldA) { this.fieldA = fieldA; } 
    public String getFieldA() { return fieldA; } 
    public void setFieldB(Future<String> fieldB) { this.fieldB = fieldB; } 
    public Future<String> getFieldB() { return fieldB; } 
} 

Вы бы затем структурировать метод ответа, как это:

public Output respond(final Input input) { 
    Output output = new Output(); 
    String fieldA = ... 
    output.setFieldA(fieldA); 

    Future<String> fieldBFuture = executor.submit(new Callable<String>() { 
    public String call() { 
     String fieldB = ... 
     return fieldB; 
    } 
    } 

    output.setFieldB(fieldBFuture); 
} 

И использовать Output объект:

Input input = ... 
Output output = respond(input); 

String fieldA = output.getFieldA(); 
// do some work 
String fieldB = output.getFieldB().get(); 
+1

@toluju Я не понимал, как «Исполнители могут быть полезны в этом контексте - можете ли вы изложить алгоритм? –

+0

@binil Добавленный пример для вас, дайте мне знать, если это прояснит ситуацию. – toluju

+0

toluju, binil, упомянутый выше, что «Выход» предоставляется сторонней библиотекой, к которой у него нет доступа к источнику; похоже, что этот метод потребует от него возможности модифицировать класс, который он не может сделать. – delfuego

0

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

+1

«Предварительно определено» Я имел в виду, что они не зависят от 'Input', но могут быть определены даже до того, как кто-то называет' response() '. –

+0

Разногласия между ответами() –

1

Почему бы не создать фабрику создания объекта, которая в процессе сборки предварительно заполняет внутреннее хранилище объектов Output, доставляет их по запросу и содержит логику, чтобы знать, когда необходимо повторно заполнить внутренний магазин? Возможно, что-то вроде этого (обратите внимание, что это просто простой код, но есть четкие места, где вы можете сделать намного больше, например, иметь определенные алгоритмы, чтобы определить, когда в пуле слишком мало объектов, синхронизировать на заселении и т.д.):

public class OutputFactory { 

    private List<Output> outputPool = new LinkedList<Output>(); 

    public OutputFactory() { 
     this.populatePool(); 
    } 

    private void populatePool() { 
     // spawn a new thread here, if you wish, to optimize time-to-return 
     for (int i = 0; i < 100; i++) { 
      outputPool.add(new Output()); 
     } 
    } 

    public Output getNewOutput() { 
     Output newOutput = outputPool.remove(0); 
     if (outputPool.size() < 10) { 
      populatePool(); 
     } 
     return newOutput; 
    } 

} 
+1

Это блокирует каждый 100-й запрос, когда пул должен быть перезаправлен, и это не намеренное поведение. Я думаю, именно поэтому Asker ссылается на другой поток для Output-generation, так что каждый запрос() возвращается как можно быстрее, а не только 99 из 100. – Kosi2801

+0

Kosi2801, вот почему я сказал, что вы можете создать новый поток там, если хотите и немедленно вернуться; Я просто написал это как псевдо/простой код, чтобы передать всю идею. – delfuego

+0

Извините, не обратил на это внимания. – Kosi2801

1

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

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

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

Любая из этих стратегий позволит вам ускорить метод respond() без накладных расходов отдельного потока.

+0

@benzado Вы правы, что выделение будет занимать одинаковое количество времени, независимо от того, какой поток я делаю. Я пытаюсь улучшить воспринимаемую производительность метода response(), выполнив распределение раньше. Подумайте об этом, как о ресторане быстрого питания. Еда потребляет столько же времени, чтобы готовить, независимо от того, когда вы ее готовите, но клиенты воспринимают более быстрое обслуживание, если у вас есть еда, приготовленная досрочно. –

+0

Это не «воспринимаемая» производительность, а латентность, которую вы пытаетесь улучшить. В любом случае, оба метода, которые я предложил, должны помочь в этом отношении. – benzado

2

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

Смежные вопросы