LINUX.ORG.RU

Java - стоит ли использовать Future/ExecutorService ?

 


0

3

Привет. Такой вопрос. Есть тред, в нем есть цикл в котором он разбирает BlockingQueue, берет оттуда задания и обрабатывает. Появилась необходимость в некоторых случаях получать результат обработки этих заданий.

Джава я знаю не очень хорошо, так что сильно не пинайте ;)

Из того что есть в java я нашел Future, но как я понял для них придется все несколько переделать - нужно будет делать отдельный Callable для каждого задания, отправлять этот Callable в ThreadExecutor и получать обратно объект Future, с помощью которого можно дождаться обработки задания.

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

Не возникнет ли тут лишнего оверхеда?

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

Какие есть еще хорошие практики/классы :) чтобы получать результат от тредов?

Спасибо!

★★★★

Не возникнет ли тут лишнего оверхеда?

Нет.

migesok
()

оверхед познается в сравнении. Если у тебя там рядом обращения в БД или вебня отрабатывают в 1000 раз дольше, то никакого оверхеда нету. Если эта BlockingQueue - это всё что есть в твоей софтине - оверхед есть. Некоторые люди доходят до такого байтоебства, что перестают использовать String например, ибо String - оверхед. Это ты тот человек, который определяет, с чем сравнивать.

возможно, тебе нужно совсем не BlockingQueue. А может быть, Disruptor. Или вообще ActiveMQ, если там работа с сетью. Без описания глобальной задачи ответ может быть любой

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

Некоторые люди доходят до такого байтоебства, что перестают использовать String например, ибо String - оверхед.

А причем тут байто*бство, если в определенных ситуациях это реально имеет дикий овер? Например, случай с конкатенацией в цикле. Что, не стоит парить себе мозг StringBuilder/StringBuffer/etc, а просто использовать перегруженный «+»? Т.е. заведомо юзать 3 операции, вместо одной append? Да ну, в [censored] такое отношение, если это у вас, погромистов, называется байто*бством и считается чем-то плохим.

znenyegvkby
()
Ответ на: комментарий от takino

Вполне возможно, но я говорю о понятии байто*бства вообщем. Этот же вопрос (string – char), который вы упомянули, был актуален вплоть до JDK7(х32) (раздел анатомии сложных структур данных):

http://www.ibm.com/developerworks/library/j-codetoheap/#N101DC

когда овер был действительно очевиден. Поэтому о так называемом байто*бстве стоит хотя-бы задуматься и не считать это чем-то плохим. Еще лучше – называть это не «байто*бством», а «хорошей практикой». А то потом каждый третий создает эти трололо-треды, а-ля «vm жрет всю раму, азаза».

znenyegvkby
()
Ответ на: комментарий от znenyegvkby

Байтоебство - это экономия на спичках, и это устоявшийся термин. К StringBuilder vs String это отношения не имеет.

takino ★★★★★
()
Ответ на: комментарий от takino

Байтоебство - это экономия на спичках

Вот если бы оно всегда было так, то хорошо. Лично мне уже не раз доводилось видеть по работе, как погромисты забивают не на пару коробков, а пропускают эти «спички» целыми ящиками. А потом могут легко списать все на байтодрочество, да еще искренне не понимать почему их код называют говном.

и это устоявшийся термин

Вот бы все это понимали...

znenyegvkby
()

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

foror ★★★★★
()
Последнее исправление: foror (всего исправлений: 1)

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

Перед отправкой достаешь свободный объект из пула и инициализируешь. Затем ждешь обработки. После кладешь обратно в пул.

foror ★★★★★
()
Последнее исправление: foror (всего исправлений: 3)

Не возникнет ли тут лишнего оверхеда?

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

ya-betmen ★★★★★
()
Ответ на: комментарий от znenyegvkby

Плюс конпелируется в билдер,нет причин не использовать плюс. Это как раз отличная иллюстрация, что происходит, когда человек пытается оптимизировать ничего не измерив, без проверки поверив в байки из интернетов

stevejobs ★★★★☆
()
Ответ на: комментарий от takino

Я имел в виду собственный вручную управляемый пул из чар[] (или даже unsafe?), экономящий строки появляющиеся в динамике, наппимер в поисковике типа гугла когда строки становятся 80% рамы. Помойму у Елизарова есть на эту тему доклад, они тогда круто сэкономили. Но слепо повторять такое без измерений - это байтоебство в его плохом, бессмысленном и беспощадном виде, имхо.

stevejobs ★★★★☆
()
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от foror

Вчера ночью сделал так - в очередь кладу объекты Map.Entry, ключ это строка-задание, и жду на объекте ответа с помощью wait, в треде кладу в entry результат, и делаю notify.

Подозреваю что futuretask делает что то примерно такое же.

OxiD ★★★★
() автор топика
Ответ на: комментарий от stevejobs

Плюс конпелируется в билдер

Именно так, но я говорил именно о ситуации с циклом, вот же

Например, случай с конкатенацией в цикле.
просто использовать перегруженный «+»?

Т.е. я об этом надуманном примере, когда в случае с append байткод выглядит так:

private static void append() {
   StringBuilder s = new StringBuilder();
   for(int i=0; i<50000; i++) {
      s.append("test");
   }
}
  private static append()V
   L0
    LINENUMBER 20 L0
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ASTORE 0
   L1
    LINENUMBER 21 L1
    ICONST_0
    ISTORE 1
   L2
   FRAME APPEND [java/lang/StringBuilder I]
    ILOAD 1
    LDC 50000
    IF_ICMPGE L3
   L4
    LINENUMBER 22 L4
    ALOAD 0
    LDC "test"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    POP
   L5
    LINENUMBER 21 L5
    IINC 1 1
    GOTO L2
   L3
    LINENUMBER 24 L3
   FRAME CHOP 1
    RETURN
   L6
    LOCALVARIABLE i I L2 L3 1
    LOCALVARIABLE s Ljava/lang/StringBuilder; L1 L6 0
    MAXSTACK = 2
    MAXLOCALS = 2
А в случае с concat так
private static void concat() {
    String s = "";
    for(int i=0; i<50000; i++) {
        s= s + "test";
    }
}
  private static concat()V
   L0
    LINENUMBER 28 L0
    LDC ""
    ASTORE 0
   L1
    LINENUMBER 29 L1
    ICONST_0
    ISTORE 1
   L2
   FRAME APPEND [java/lang/String I]
    ILOAD 1
    LDC 50000
    IF_ICMPGE L3
   L4
    LINENUMBER 30 L4
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "test"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 0
   L5
    LINENUMBER 29 L5
    IINC 1 1
    GOTO L2
   L3
    LINENUMBER 32 L3
   FRAME CHOP 1
    RETURN
   L6
    LOCALVARIABLE i I L2 L3 1
    LOCALVARIABLE s Ljava/lang/String; L1 L6 0
    MAXSTACK = 2
    MAXLOCALS = 2
Т.е. преобразовывается-то все как надо, но оверхед получается от создания на каждой итерации нового объекта. И почему-то из кода в реальной жизни, я иногда замечаю что не всем это очевидно почему-то, хотя ситуация и действительно нагло высосана мною из пальца. Т.е. в данном конкретном случае оверхед будет прямо скажем очевиден:
long nmillis = System.currentTimeMillis();
concat();
System.out.println("concat: " + (System.currentTimeMillis() - nmillis) + " ms"); //concat: 7925 ms
...
nmillis = System.currentTimeMillis();
append();
System.out.println("append: " + (System.currentTimeMillis() - nmillis) + " ms"); //append: 2 ms

znenyegvkby
()

Есть тред, в нем есть цикл в котором он разбирает BlockingQueue, берет оттуда задания и обрабатывает

Надеюсь, вы делаете это не сами? Смотрели ли ForkJoinPool?

bytecode ★★
()
Ответ на: комментарий от znenyegvkby

Мужык, разупорись. Создание объекта для сабмита задачи в пул потоков это капля в море по сравнению с оверхедом на межпоточное взаимодействие. Сравнение со StringBuilder'ом тут неуместно, там расходы сравнимы.

migesok
()
Ответ на: комментарий от migesok

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

Поехавший? :) Я где-то говорил об этом? Если ты не заметил, я вообще отвечал исключительно на это, не касаемо треды

Некоторые люди доходят до такого байтоебства, что перестают использовать String например, ибо String - оверхед.

Так что советую принять разупорина тебе :)

znenyegvkby
()
Ответ на: комментарий от znenyegvkby

Прости, братиш, бес попутал.

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

migesok
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.