Обход ошибки «возможно, переменная не инициализирована» - PullRequest
3 голосов
/ 06 марта 2012
public final void sendAdvertisement(final Advertisement advertisment, int delay, final int repetitions){
    final ScheduledFuture exec = executor.scheduleAtFixedRate( //<< initialized on this line
        new Runnable(){
            int totalSends = 0;
            public void run(){
                //do stuff here

                if(++totalSends >= repetitions) exec.cancel(true); //<< here is says exec might not be initialized
            }
        },
    0, delay, TimeUnit.MILLISECONDS);
}

Если это невозможно, не могли бы вы предложить лучший способ сделать это?Я не смог найти метод для этого в ScheduledThreadPoolExecutor.По сути, я пытаюсь запустить этот код 3 раза, а затем отменить «таймер».Возможно, я мог бы использовать Swing Timer, но я не хочу, потому что он используется для других вещей.

В комментариях говорится, но это ошибка:

%PATH%Discovery.java:148: variable exec might not have been initialized
                if(++totalSends >= repetitions) exec.cancel(true);

Ответы [ 3 ]

2 голосов
/ 06 марта 2012

Ваш код может работать с точки зрения удаления предупреждения компилятора, но весь смысл предупреждения указывает на то, что вы, возможно, обращаетесь к переменной, которая еще не была назначена.Даже если exec или exec[0] не равен NULL, также нет гарантии, что объект ScheduledFuture был даже правильно инициализирован - да, даже если внутренний поток может быть запущен.Это очень опасно и может работать некоторое время, но затем резко потерпит неудачу в работе, когда вы переходите на архитектуру с большим количеством ядер или при других условиях нагрузки.Это также может работать, но затем вы изменяете свой код do stuff here через месяц, и он начинает давать сбой.

Я вижу несколько способов, которыми вы можете сделать это лучше.Они более сложные, но также более безопасные и совместимые с Java.Первое, что приходит на ум, это использование AtomicReference:

final AtomicReference<ScheduledFuture> futureReference =
    new AtomicReference<ScheduledFuture>();
ScheduledFuture exec = executor.scheduleAtFixedRate(
    new Runnable() {
        int totalSends = 0;
        public void run() {
            //do stuff here
            if (++totalSends >= repetitions) {
                // we need to wait for the future to be initialized
                while (futureReference.get() == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        Thread.currentThread.().interrupt();
                    }
                }
                futureReference.get().cancel(true);
            }
        }
    },
    0, delay, TimeUnit.MILLISECONDS);
futureReference.set(exec);
1 голос
/ 06 марта 2012

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

for (int i = 0; i < iterations; i++) {
    executor.schedule(new Runnable() {
        public void run() {
            // do stuff here
        }
    }, delay * i, TimeUnit.MILLISECONDS);
}

Как сказал УолтерМ: это не очень хороший способ создавать много новых экземпляров, используйте ссылку в цикле.

1 голос
/ 06 марта 2012

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

Первым решением было бы сделать exec окончательным одноэлементным массивом. Затем вы можете назначить exec [0] = что-то после объявления, даже если сам массив является окончательным. Вариант этого заключается в использовании / создании некоторого ссылочного класса (поскольку вы можете изменять атрибуты конечных ссылок, но не сами ссылки). Ниже приведен простой пример, но имейте в виду, что он не учитывает никаких проблем параллелизма (см. Далее):

    final ScheduledFuture[] exec = new ScheduledFixture[1];
    exec[0] = executor.scheduleAtFixedRate( //<< initialized on this line
            new Runnable(){
                int totalSends = 0;
                public void run(){
                    //do stuff here

                    if(++totalSends >= repetitions) exec[0].cancel(true); //<< here is says exec might not be initialized
                }
            },
        0, delay, TimeUnit.MILLISECONDS);

В качестве альтернативы вы можете вывести exec из локальной области видимости метода и вместо этого сделать его атрибутом класса.

Однако я должен предупредить вас, что, особенно при начальной нулевой задержке, существует реальная вероятность того, что код внутри исполняемого файла будет выполнен до того, как метод scheduleAtFixedRate вернется, и в этом случае exec [0] будет по-прежнему нулевым. Кроме того, вы должны использовать синхронизацию, чтобы гарантировать, что значение exec [0], установленное основным потоком, будет доступно для потока, ответственного за выполнение runnable.

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

...