Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce(new IntBinaryOperator() {
                    @Override
                    public int applyAsInt(int n, int m) {
                        return sum(n, m);
                    }
                })
                .getAsInt();
    }

    private static int sum(int n, int m) {
        return n + m;
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}


на моей машине это код ИНОГДА заканчивается дедлоком, а как иногда проходит нормально. Как это можно объяснить?
10 дек 18, 16:36    [21759326]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
Если метод sum сунуть внутрь IntBinaryOperator, то дедлок не случается

public class Test {
    static {
        final int SUM = IntStream.range(0, 100)
                .parallel()
                .reduce(new IntBinaryOperator() {
                    private int sum(int n, int m) {
                        return n + m;
                    }
                    @Override
                    public int applyAsInt(int n, int m) {
                        return sum(n, m);
                    }
                })
                .getAsInt();
    }



    public static void main(String[] args) {
        System.out.println("Finished");
    }
}
10 дек 18, 16:52    [21759341]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
mad_nazgul
Member

Откуда:
Сообщений: 4912
questioner,

В первом случае sum - статичный метод.
Во втором - нет.

По этому в первом deadlock, а во втором нет. :-)
11 дек 18, 07:58    [21759778]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
mad_nazgul
questioner,

В первом случае sum - статичный метод.
Во втором - нет.

По этому в первом deadlock, а во втором нет. :-)


А почему в первом случается ИНОГДА, а не всегда?
11 дек 18, 09:55    [21759857]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
mad_nazgul
Member

Откуда:
Сообщений: 4912
questioner
mad_nazgul
questioner,

В первом случае sum - статичный метод.
Во втором - нет.

По этому в первом deadlock, а во втором нет. :-)


А почему в первом случается ИНОГДА, а не всегда?


Потому что, как повезет. :-)
11 дек 18, 11:32    [21760005]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
mad_nazgul
questioner
пропущено...


А почему в первом случается ИНОГДА, а не всегда?


Потому что, как повезет. :-)


А можете объяснить кейс при котором везёт?
11 дек 18, 11:44    [21760025]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
Можно даже ещё проще ту же проблему сделать:

public class Test {
    static {
        System.out.println("static initializer: " + Thread.currentThread().getName());

        final long SUM = IntStream.range(0, 5)
                .parallel()
                .mapToObj(i -> {
                    System.out.println("map: " + Thread.currentThread().getName() + " " + i);
                    return i;
                })
                .count();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}


Точно такая же ерунда - иногда нормально проходит, а иногда(почти всегда) зависает
11 дек 18, 13:08    [21760192]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
dimonz80
Member

Откуда:
Сообщений: 200
questioner
Можно даже ещё проще ту же проблему сделать:

public class Test {
    static {
        System.out.println("static initializer: " + Thread.currentThread().getName());

        final long SUM = IntStream.range(0, 5)
                .parallel()
                .mapToObj(i -> {
                    System.out.println("map: " + Thread.currentThread().getName() + " " + i);
                    return i;
                })
                .count();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}


Точно такая же ерунда - иногда нормально проходит, а иногда(почти всегда) зависает


  /**
     * Prints a String and then terminate the line.  This method behaves as
     * though it invokes <code>{@link #print(String)}</code> and then
     * <code>{@link #println()}</code>.
     *
     * @param x  The <code>String</code> to be printed.
     */
    public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
11 дек 18, 14:42    [21760365]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
dimonz80
+
questioner
Можно даже ещё проще ту же проблему сделать:

public class Test {
    static {
        System.out.println("static initializer: " + Thread.currentThread().getName());

        final long SUM = IntStream.range(0, 5)
                .parallel()
                .mapToObj(i -> {
                    System.out.println("map: " + Thread.currentThread().getName() + " " + i);
                    return i;
                })
                .count();
    }

    public static void main(String[] args) {
        System.out.println("Finished");
    }
}


Точно такая же ерунда - иногда нормально проходит, а иногда(почти всегда) зависает


  /**
     * Prints a String and then terminate the line.  This method behaves as
     * though it invokes <code>{@link #print(String)}</code> and then
     * <code>{@link #println()}</code>.
     *
     * @param x  The <code>String</code> to be printed.
     */
    public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }




Ну это всё прям объясняет
11 дек 18, 16:08    [21760521]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
Пылинка
Member

Откуда: СПб
Сообщений: 359
Потому что Fork/Join имеет скорее академический интерес, реально не может управлять внутренними join. "result in a stall (thread in a wait state) when the recursion level becomes long." Годится для быстрых операций (убрали SUM() ВНУТРЬ - работает). Никаких блокировок, вводов/выводов и методов синхронизации использовать нельзя.
PS По-моему, это исключает серьезное использование многопоточных stream, основанных на F/J, а так, побаловаться на собеседовании.
11 дек 18, 22:02    [21760812]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
maxkar
Member

Откуда:
Сообщений: 154
questioner
на моей машине это код ИНОГДА заканчивается дедлоком, а как иногда проходит нормально. Как это можно объяснить?


Ну уже правильно объяснили - везет иногда :). Это пример на class initialization deadlock из вашей соседней темы (правда в данном случае - с самим собой). Ну т.е. main->stream->workers done->some (non-current thread) worker->main. Плюс особенности реализации Fork/Join где вызывающий поток тоже выполняет вычисления. И некоторая задержка на инициализацию пула, потоков и всех остальных локов в worker thread.

В первом случае вы вызваете sum на Test, который еще не до конца инициализирован (не отработал static initializer). Поэтому все потоки, обращающиеся к классу Test (кроме main thread) будут ждать. Для FJP - все потоки кроме main при вызове sum будут ждать завершения инициализаци Test, которая в свою очередь ждет завершения вычислений. Если вдруг main успел все обсчитать до других потоков (у него лок на инициализацию Test) - тогда все работает. Увеличьте range или добавьте sleep небольшой - будет стабильнее блокироваться.

Во втором примере у вас анонимный класс. Его экземпляр создается в main потоке (все необходимые обращения к Test - допускаются). Экземпляр самодостаточный (к классу Test он не обращается), поэтому все workers могут отработать нормально.

Третий пример (с лямбдой) полностью аналогичен первому примеру с дополнительной задержкой. Курите javap -private -v Test. Интересные места - метод lambda$static$0 и BootstrapMethods (которое в конце). В переводе на человеческий, на месте лямбды в коде будет сгенерирован класс, реализующий IntFunction и вызывающий Test.lambda$static$0.
11 дек 18, 22:46    [21760859]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
maxkar,

автор
В первом случае вы вызваете sum на Test, который еще не до конца инициализирован (не отработал static initializer). Поэтому все потоки, обращающиеся к классу Test (кроме main thread) будут ждать. Для FJP - все потоки кроме main при вызове sum будут ждать завершения инициализаци Test, которая в свою очередь ждет завершения вычислений. Если вдруг main успел все обсчитать до других потоков (у него лок на инициализацию Test) - тогда все работает. Увеличьте range или добавьте sleep небольшой - будет стабильнее блокироваться.


Вы будете удивлены, но иногда дедлока не случается даже в случае, когда воркеры обсчитывают лямбду
12 дек 18, 12:56    [21761393]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
alex55555
Member

Откуда:
Сообщений: 2128
questioner
Вы будете удивлены, но иногда дедлока не случается даже в случае, когда воркеры обсчитывают лямбду

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

Повторюсь - вам сначала читать научиться надо.
12 дек 18, 14:36    [21761607]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
alex55555
questioner
Вы будете удивлены, но иногда дедлока не случается даже в случае, когда воркеры обсчитывают лямбду

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

Повторюсь - вам сначала читать научиться надо.


И что же автор конкретно не понимает?
12 дек 18, 15:21    [21761696]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
Пылинка
Member

Откуда: СПб
Сообщений: 359
maxkar
В первом случае вы вызваете sum на Test, который еще не до конца инициализирован (не отработал static initializer). Поэтому все потоки, обращающиеся к классу Test (кроме main thread) будут ждать. Для FJP - все потоки кроме main при вызове sum будут ждать завершения инициализаци Test, которая в свою очередь ждет завершения вычислений. Если вдруг main успел все обсчитать до других потоков (у него лок на инициализацию Test) - тогда все работает. Увеличьте range или добавьте sleep небольшой - будет стабильнее блокироваться.
Не могу согласиться, работают все потоки (у меня их 8, включая main), поэтому при range=100 у меня вообще никак не падает. ЗАТО ПРИ УВЕЛИЧЕНИИ RANGE стабильно падает после выполнения примерно 80-83% работы (r=400 -> 335, r=1000 -> 830 операций, при r=10 000 немного больше 8 000), видимо начинается "кража работы".
12 дек 18, 16:15    [21761794]     Ответить | Цитировать Сообщить модератору
 Re: Почему этот код иногда приводит к дедлоку?  [new]
questioner
Member

Откуда:
Сообщений: 1865
Пылинка,

https://bugs.openjdk.java.net/browse/JDK-8215634
2 янв 19, 20:36    [21777668]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить