2013-09-24 5 views
12

В настоящее время я изучаю параллелизм Java. И я очень удивлен тем, как ведет себя код.Странное поведение кодового кода

import java.util.concurrent.*; 

public class Exercise { 
    static int counter = 0; 

    static synchronized int getAndIncrement() { 
     return counter++; 
    } 

    static class Improper implements Runnable { 

     @Override 
     public void run() { 
      for (int i = 0; i < 300; i++) { 
       getAndIncrement(); 
      } 
     } 
    } 


    public static void main(String[] args) { 
     ExecutorService executorService = Executors.newFixedThreadPool(3); 
     for (int i = 0; i < 300; i++) { 
      executorService.submit(new Improper()); 
     } 
     executorService.shutdown(); 
     System.out.println(counter); 
    } 
} 

Не должно ли оно выводить 90000 все время? Вместо этого результат различается все время.

+0

Попробуйте использовать 'AtomicInteger'. –

+1

Подумайте, вам просто нужно дождаться завершения службы ExecutorService http://stackoverflow.com/questions/1250643/how-to-wait-for-all-threads-to-finish-using-executorservice –

ответ

25
  1. executorService.shutdown() не дожидаться прекращения службы. Вам нужно позвонить в awaitTermination.

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

+0

помогает ли это сделать счетчик изменчивым ? – bvk256

+1

Он делает это, но еще раз, это не позволит вам удалить синхронизацию записывающей части. Поэтому вам понадобятся оба. –

+2

@ bvk256 - Учитывая, что вы уже используете 'synchronized', было бы лучше написать синхронизированный метод getter для получения конечного результата. (Использование переменной 'synchronized' * и *' volatile' потенциально неэффективно.) –

2

Вы не ждите все ваши занесена задачи прекратить см Javadoc для ExecutorService.html#shutdown. Таким образом, получение произвольного результата каждый раз является ожидаемым поведением.

3

Вам нужно убедиться, что все задачи успели закончить. Использовать awaitTermination

public static void main(String[] args) throws InterruptedException { 
    ExecutorService executorService = Executors.newFixedThreadPool(3); 
    for (int i = 0; i < 300; i++) { 
     executorService.submit(new Improper()); 
    } 
    executorService.shutdown(); 
    executorService.awaitTermination(2, TimeUnit.SECONDS); 
    System.out.println(counter); 
} 
Смежные вопросы