2016-12-27 11 views
1

Как пример кода, как показано ниже.Запустить runnable пулом потоков, следует ли использовать volatile?

Я собираюсь использовать пул потоков для запуска объекта TestRunnable в период.

Должен ли я объявить переменную total как изменчивой?

public class TestRunnable implements Runnable { 
    private int total; 
    @Override 
    public void run() { 
     if (total > 10) { 
      return; 
     } else { 
      total += 1; 
      System.out.print("Run in times: " + total); 
     } 
    } 
} 
+0

Я думаю, что решающим моментом является: что вы на самом деле делаете ** с переменной 'total'? Если вы используете его только внутренне (для оператора печати), и если вы отправляете каждый экземпляр 'TestRunnable' только * один раз * в пул потоков, то вам не нужно' volatile'.Но ** если ** существует, например, метод getTotal, который каким-либо образом раскрывает эту переменную, тогда может потребоваться дополнительная синхронизация (или, в простейшем случае, «volatile»). – Marco13

ответ

1

Предполагая, тот факт, что переменная объявлена ​​как

private int total; 

(не статический)

Это не будет разделено между несколькими потоками (Пока вы продолжаете создавать новый экземпляр для каждого представленного потока), поэтому нет необходимости объявлять его изменчивым.

Если вы используете тот же экземпляр несколько раз - то вам следует рассмотреть возможность использования AtomicInteger вместо регулярных Int, так как операции

total+= 1; 

или

total++; 

не является атомарным что может привести к неожиданным результатам в многопоточным env.

+2

Стоит отметить, что в то самое время, когда вам действительно не нужно использовать volatile и вместо этого требуется правильная синхронизация. Или атомные типы (http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/atomic/package-summary.html). Я считаю, что volatile полезен только тогда, когда люди, которые знают, что делают, оптимизируют вещи, которые имеют решающее значение для производительности. –

+0

@NathanHughes интересно. Можете ли вы одновременно отправить один и тот же экземпляр в один и тот же пул? Я никогда не думал об этом. –

+0

Эта переменная не является статичной, но я использую пул потоков для ее выполнения. Так много потоков может получить доступ к переменной. – yunfan

0

Даже если вы отметите поле volatile, это ничего не стоило бы, поскольку инструкция if не является атомной. Рассмотрим следующую ситуацию:

  1. Нить выполняет run() метод TestRunnable «s.
  2. Выражение total > 10 имеет значение, потому что falsetotal 10.
  3. В результате, поток поступает в else ветвь и пробует, чтобы увеличить переменную.
  4. Другая нить делает то же самое, но total еще не увеличен.
  5. Теперь оба потока увеличивают total, что может оставить его в противоречивом состоянии.

Обратите внимание, что это может произойти только в том случае, если вы отправляете один и тот же экземпляр несколько раз в пул (по крайней мере, это единственная ситуация, о которой я могу думать). Однако, поскольку вы управляете состоянием в этом Runnable, что кажется мне неправильным - это, вероятно, то, что вы делаете.

Как указано @DmitriyKotov, вы должны перейти на AtomicInteger. С Java 8 вы можете легко:

total.getAndUpdate(currentVal -> currentVal > 10 ? currentVal : currentVal + 1); 
+0

Только один поток будет выполнять код за один раз. – yunfan