2012-03-06 3 views
3
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); 
+0

final ScheduledFeature exec = null; exec = executor.sche ... и т. д. Затем в вашем операторе if (++ totalSends) добавьте вторичный оператор if (exec! = null) {exec.cancel (true);} ??? –

+0

Это невозможно, вы не можете изменить конечную переменную после ее инициализации, даже до нулевого права? – WalterM

+0

Куда вы правы. Сделайте это не окончательным. –

ответ

2

Ваш код может работать с точки зрения удаления предупреждения компилятора, но вся точка предупреждения указывает, что вы можете получить доступ к переменной, которая еще не назначена. Даже если exec или exec[0] не является нулевым, также нет гарантии, что объект 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); 
+0

Да, это почти то же самое, хотя, я сделал проверку if (exec [0] == null); лично я думаю, что это одно и то же. Я никогда не буду использовать Thread.sleep(); в threadpool. Я думаю, что это плохая идея. – WalterM

+0

'== null' - это не то же самое, чувак. Оптимизатор может вернуть значение из инициализатора, но позже будет выполнять фактическую инициализацию кода. Это то же самое, что и ошибка блокировки двойной проверки: http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html – Gray

+0

Что касается сна, лично я считаю, что вращение _much_ более опасным, чем сон. Вы можете сделать «sleep (1)», если вам действительно нужно. – Gray

1

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

Первым решением было бы сделать 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] будет по-прежнему иметь значение null. Кроме того, вы должны использовать синхронизацию, чтобы гарантировать, что значение exec [0], заданное основным потоком, будет доступно потоку, ответственному за выполнение runnable.

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

+0

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

+0

Извините, но я действительно не согласен с этим ответом. Он может решить проблему компиляции, но нет guantee, что 'exec [0]' не является нулевым, и даже если он не является нулевым, объект может не заполняться инициализированным, даже если запущен «Runnable». Это своего рода решение, которое может хорошо работать, а затем безуспешно работать в процессе производства, при различной нагрузке или после изменения того, как много делается в «делах здесь». – Gray

+0

Вот почему я специально предупреждал о таких проблемах во втором абзаце. Конечно, использование AtomicReference, как вы предложили, безусловно, намного лучше во всех отношениях. Я не знаю, как я так долго пропустил этот класс. – Jiddo

1

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

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

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

+0

Это хорошая идея, но я не хочу создавать новую Runnable для каждой итерации. Особенно, если он может пройти более 10 или что-то в этом роде. Его слишком много. – WalterM

+0

Теперь, когда я думаю об этом, ты на самом деле прав. Если вы создаете Runnable перед циклом, а затем просто передаете один и тот же объект Runnable через каждую итерацию. На самом деле вам не нужен новый. – WalterM

+0

Да, конечно - я нацелен на проблему исполнения, а не на проблему создания множества экземпляров. Как вы, наконец, решили проблему? –

Смежные вопросы