2016-03-20 7 views
1

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

Источник:

class MyValueHolder { 

    public static ThreadLocal<Integer> value = new ThreadLocal<Integer>() 
    { 
     protected Integer initialValue() { 
      return 0; 
     } 
    }; 

    public static void increment() { 
     value.set(value.get() + 1); 
    } 

    public static Integer get() { 
     return value.get(); 
    } 
} 

class MyTask implements Runnable { 

    public static int counter; 

    public MyTask() { 
     counter++; 
    } 

    public void run() { 

     for(int i = 0; i < 100; i++) { 
      MyValueHolder.increment(); 
     } 
     System.out.println("MyTask " + counter + ", Value " + MyValueHolder.get()); 
    } 
} 

public class Main { 

    public static void main(String[] args) { 

     ExecutorService es = Executors.newCachedThreadPool(); 

     for(int i = 0; i < 5; i++) { 

      es.execute(new MyTask()); 
     } 

     es.shutdownNow(); 
    } 
} 

После завершения выход большую часть времени это:

MyTask 5, Value 100 
MyTask 5, Value 100 
MyTask 5, Value 100 
MyTask 5, Value 100 
MyTask 5, Value 100 

Что то, что я ожидал увидеть.

Однако, иногда я получаю что-то вроде этого:

MyTask 2, Value 100 
MyTask 4, Value 200 
MyTask 5, Value 300 
MyTask 5, Value 100 
MyTask 5, Value 100 

Теперь это сбивает с толку. Почему значения различаются, хотя каждая задача делает абсолютно то же самое?

+0

Порядок, в котором выполняются потоки, не является ** гарантированным порядком, в котором они были запущены. Если ваши потоки должны выполняться в определенном порядке, используйте 'wait' /' notify' или ['CountDownLatch'] (https://docs.oracle.com/javase/7/docs/api/java/util/ одновременное/CountDownLatch.html). – Majora320

ответ

5

CachedThreadPoolExecutor повторно использует потоки, если Runnable заканчивается, прежде чем вы добавите все Runnable s. Если это произойдет, то ThreadLocal этого потока также будет повторно использоваться.

0

потоки будут продолжать работать, пока вы получите выход, вы должны дождаться их завершения, вы можете попробовать isAlive(); с резьбой

1

Как указано выше, ThreadLocal используется повторно.

Для счетчика используйте AtomicInteger и установите начальное значение. counter ++ - не атомная операция, две ее операции и не являются потокобезопасными.

+0

«counter» в настоящее время не является проблематичным, так как его значение увеличивается в конструкторе - и конструктор вызывается только из основного метода. –

+0

Извините, вы явно правы, счетчик печатает неправильные номера, потому что метод запуска потока завершен до того, как все вызовы будут вызваны. – LiozM