Pull to refresh

Comments 23

UFO just landed and posted this here
Здесь стоит учитывать какие задания будет выполнять пул потоков. Если их немного или они продолжительные, то в этом случае накладными расходами можно пренебречь. Если заданий много и они быстрые, то накладные расходы могут занимать достаточно много времени. Это касается не только стектрейса, но и любых вычислений внутри пула потоков.
Что касается исключений, то наверно стоит рассчитывать на то, что их будет немного и это не ожидаемое поведение, поэтому в этом случае можно логировать и клиентский стектрейс.
UFO just landed and posted this here
Я имел в виду именно захват текущего стектрейса при создании задания (метод clientTrace). Он будет выполняться всегда, вне зависимости от того, будет исключение или не будет.

Да, здесь ничего не поделаешь. Однако, можно создать разные оболочки для различных сценариев. В тестовой среде можно поддерживать максимум логирования, в том числе клиентского стектрейса. С помощью DI можно легко выбрать нужную реализацию.
По пункту 7: никогда не используйте System.currentTimeMillis() для измерения длительности операций.
Для этого предназначен System.nanoTime().

System.currentTimeMillis() возвращает текущее время (wall-clock). Из-за этого можно внезапно получить слишком большую или вообще отрицательную длительность измеряемой операции, если между вызовами System.currentTimeMillis() изменилось время (перевелись часы на зимнее/летнее время, скорректировалось времени по протоколу NTP, администратор поправил часы и т.п.).

С другой стороны System.nanoTime() возвращает время в наносекундах, прошедшее после какого-то фиксированного события (старт JVM, старт операционной системы). Оно никак не связано с текущим временем и может быть отрицательным. System.nanoTime() предназначен только для измерения прошедшего времени. Хоть разрешение и наносекундное, не стоит расчитывать на такую точность — максимум на микросекундную.
есть проблема, связанная с использованием nanoTime из двух разных нитей — используется ядерный счетчик, который может отличаться от ядра к ядру.
В какой реализации JVM? HotSpot 1.7 гарантирует единственный источник в пределах одного инстанса JVM.
Кстати… поясните как это может System.currentTimeMillis() вернуть отрицательные значения при переводе на летнее/зимнее время?

Летнее/Зимнее время в Европе

final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam"));

final Date date = sdf.parse("2015-03-29 01:59:59.999 CET");
System.out.println(sdf.format(date) + " : " + date.getTime());
final Date date2 = new Date(date.getTime() + 1L);
System.out.println(sdf.format(date2) + " : " + date2.getTime());

и результат будет
2015-03-29 01:59:59.999 CET : 1427590799999
2015-03-29 03:00:00.000 CEST : 1427590800000


Т.е. нет никаких прыжков во времени связанные с переходом на зимнее/летнее время ибо
   /**
     * Returns the current time in milliseconds.  Note that
     * while the unit of time of the return value is a millisecond,
     * the granularity of the value depends on the underlying
     * operating system and may be larger.  For example, many
     * operating systems measure time in units of tens of
     * milliseconds.
     *
     * <p> See the description of the class <code>Date</code> for
     * a discussion of slight discrepancies that may arise between
     * "computer time" and coordinated universal time (UTC).
     *
     * @return  the difference, measured in milliseconds, between
     *          the current time and midnight, January 1, 1970 UTC.
     * @see     java.util.Date
     */
    public static native long currentTimeMillis();


в «часовом поясе» UTC нет зимнего или летнего времени.
Смещение локального времени хоста относительно UTC может меняться, время на хосте (даже если он живёт по UTC) может меняться.

Допустим, первый вызов currentTimeMillis() был сделан в 00:01:00 UTC, часы спешили и администратор (или ntpd) сделали adjtime до 00:00:00 UTC. Что вернёт currentTimeMillis() после этого?
Заметьте — я нигде не говорил про случай с NTP, администратором или другими событиями — только о случае, когда все идет ровно, кроме того, что есть момент перехода на летнее или зимнее время.
Ибо я не собираюсь спорить, что в случае с ntp возможны сюрпризы.
Я вас неправильно понял. Если оставить только стационарный переход зима/лето, то разная версия tzdata в системе и jdk может дать схожую проблему со скачком на час.
Откуда разница и причем тут tzdata, если currentTimeMillis выдает время в UTC?
Посмотрел на исходники os::javaTimeMillis(), на linux используется голый gettimeofday(3p), так что tzdata, зашитая в jvm роли не играет.

Предполагал, что jvm конвертирует локальное время с учётом таймзоны при вызове System.currentTimeMillis(). Был неправ.
Ну значит при переходе зимнее/летнее отрицательной разницы не случится, раз результат возвращается в UTC.
Обратите внимание, что блокирующий ввод/вывод не пробрасывает InterruptedException (что прискорбно)

Не совсем так: InterruptedIOException
emails.parallelStream().forEach(this::sendEmail);

Так нельзя делать. По-умолчанию оно использует docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html#commonPool--
А это значит, что длительная операция может затормозить другие операции, и, к примеру, если в другом месте используется parallelStream, то там могут быть задержки.
В Java 8 был представлен более мощный класс CompletableFuture. Пожалуйста, используйте его там, где это возможно.


вот «где возможно» все же не стоит — т.к. опять же (как и в треде выше) эти все новые удобные операции реализованы через отправку задач в ForkJoinPool.html.commonPool(), который используется еще много для чего, соотв-но пихать все туда без разбора — рискованно.
В интерфейсе CompletableFuture вполне есть возможность указать Executor для выполнения соответствующих действий. А так async tasks выполняются на FJP.commonPool или в своих тредах (если его parallelism < 2, что проиходит на машинах с 1 и 2 виртуальными ядрами или при соответствующей настройке).
Интересно, а вы пробовали, что реально произойдёт, а не в теории?
У вас написано:

final Future<Integer> division = executorService.submit(() -> 1 / 0);
//ниже будет выброшено ExecutionException, вызванное ArithmeticException
division.get();

Примечательно, что даже в Spring framework допустили эту ошибку в Async, см.: SPR-8995 и SPR-12090.


Давайте вызовем:

    public static void main(String[] args) {
        try {
            System.out.println(
                    Executors.newSingleThreadExecutor()
                             .submit(() -> 1.0d / 0).get()
            );
        } catch (Throwable tr) {
            System.out.println(tr.getLocalizedMessage());
        }
    }


Вы пишете об исключении… Уупс — результат будет Infinite значение Double, а не ExecutionException, как пишете Вы
Так, прошу прощения — я тут ошибся — я double вставил, а он при делении на 0 втыкает Infinite без исключения… Лажанул — простите.
Вы пишите, что CompletableFuture как работает как Future, но это не так. В CompletableFuture нет метода завершиться ожидание по таймауту. Он есть только в Java 9, а для Java 8 гуглятся только обходные пути. В момент написания статьи никакой Java 9 ещё не было, да и сейчас переход только идёт.
Sign up to leave a comment.

Articles