Java лямбда попала в тупик при запуске через статическое объявление - PullRequest
0 голосов
/ 21 февраля 2019

Следующий код меня сильно озадачивает.

import java.util.function.Predicate;

public class Test {

    private final Predicate<String> filter = s -> s != null;

    private boolean started = false;

    private class Runner implements Runnable {
        @Override
        public void run() {
            synchronized ( Test.this ) {
                started = true;
                Test.this.notifyAll();
                traverse("");
            }
        }
    }

    public Test() {
        System.out.println(filter.test(""));
        Thread thread = new Thread(new Runner());
        thread.setDaemon(true);
        thread.start();
    }

    public synchronized String start() {
        while ( !started ) {
            try {
                wait();
            } catch ( InterruptedException ex ) {}
        }
        return "";
    }

    private synchronized void traverse(String s) {
        filter.test(""); // DOES NOT COMPUTE
        filter.test(s);
        System.out.println("not here");
    }

    private static final String STRING = new Test().start(); // POS1

    public static void main(String[] args) {

        System.out.println(STRING); // POS2

    }

}

Он застрял на DOES NOT COMPUTE.Однако, если я удаляю строку POS1 и изменяю POS2 на System.out.println(new Test().start()), она работает безупречно.В приведенном выше коде filter, по-видимому, не оценивает, инициируется ли Test через статическую переменную.

Почему это так и как это можно исправить, пожалуйста?

1 Ответ

0 голосов
/ 22 февраля 2019

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

Что касается того, как это исправить, просто вызовите его в методе main, как вы сказали в вопросе.

Следующие строки взяты из JLS

Если объект Classдля C указывает, что инициализация для C выполняется другим потоком, затем отпустите LC и заблокируйте текущий поток, пока не будет сообщено, что инициализация в процессе завершена, и в этот момент повторите этот шаг.

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5 https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2 https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.1.3

...