2016-11-19 3 views
0

Это школьное задание, но мне действительно нужна помощь с этим. Я не могу за всю жизнь понять, почему две разные версии nextValue() ведут себя по-другому - одно - это потокобезопасное, а другое - нет. Может ли кто-нибудь дать мне несколько указателей в правильном направлении?Безопасная резьба по сравнению с небезопасными реализациями счетчика

Я включил обе версии в одном классе ниже, но, очевидно, они оба не присутствуют в коде ...

public class NumGenerator { 

    static final int MIN_VALUE = -256; 
    static final int MAX_VALUE = 255; 
    static final int INITIAL_VALUE = MIN_VALUE -1; 


    private final AtomicInteger counter = new AtomicInteger(INITIAL_VALUE); 
    private final AtomicInteger resetCounter = new AtomicInteger(0); 

    private final Object lock = new Object(); 

    // Thread safe 
    public int nextValue() { 
     int next = counter.incrementAndGet();  
     if (next > MAX_VALUE) {       
      synchronized (lock) {      
       next = counter.incrementAndGet(); 
       if (next> MAX_VALUE) {     
        counter.set(MIN_VALUE); 
        resetCounter.incrementAndGet(); 
        next = MIN_VALUE; 
       } 

      } 
     } 
     return next; 
    } 

    // Non thread safe 
    public int nextValue() { 
     int next = counter.incrementAndGet(); 
     if (next > MAX_VALUE) { 
      synchronized (lock) { 
       int i = counter.get(); 
       if (i > MAX_VALUE) { 
        counter.set(INITIAL_VALUE); 
        resetCounter.incrementAndGet(); 
       } 
       next = counter.incrementAndGet(); 
      } 
     } 
     return next; 
    } 
} 
+0

Логика обоих кодов выглядит по-другому. IMHO, обе функции являются потокобезопасными. Однако, как уже упоминалось, обе функции могут возвращать другой результат, чем ожидалось. –

+0

Вы правы, они возвращают разные выходы ... но почему? Для меня они выглядят функционально одинаковыми. Что мне не хватает? – BadCash

+0

'counter.get();' и 'counter.incrementAndGet(); 'разные. Таким образом, вы получите другой вывод – iNan

ответ

1

Допустим значения были: MIN_VALUE = -1, MAX_VALUE = 3 , счетчик = 3.

Код 1:

synchronized (lock) {      
       next = counter.incrementAndGet(); 
       if (next> MAX_VALUE) {     
        counter.set(MIN_VALUE); 
        resetCounter.incrementAndGet(); 
        next = MIN_VALUE; 
       } 

      } 
  1. Это приращение значение счетчика, а затем использует его для сравнения.
  2. Таким образом, значение next будет равно 4. if(next > MAX_VALUE) будет, если (4> 3) это изменит значение рядом с -1 и вернет его.

Код 2:

synchronized (lock) { 
       int i = counter.get(); 
       if (i > MAX_VALUE) { 
        counter.set(INITIAL_VALUE); 
        resetCounter.incrementAndGet(); 
       } 
       next = counter.incrementAndGet(); 
      } 
  1. Это присваивает значение, чтобы противостоять затем сравнивает.
  2. Значение i все равно будет 3. if(i > MAX_VALUE) будет, если (3> 3), что его не так возвращает 3 как выход.

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

Даже код внутри if (variable> MAX_VALUE) приведет к разному выходу.

Так что это не имеет никакого отношения к безопасности Thread.

+0

Я только что понял, в чем разница. Вы правы, что разница заключается в использовании 'IncrementAndGet()' vs, просто используя 'get()', но я не согласен с тем, что он не имеет никакого отношения к безопасности потоков.Оба кода работают в одном потоке, но при одновременном вызове есть шанс, что значение будет изменено другим потоком между первым «incrementAndGet()» и «get()», поэтому второй IF-оператор является недопустимым, поскольку он должен быть равен первой IF-заявке. В основном код должен предполагать, что 'counter' может быть чем угодно во втором IF, как если бы не было внешнего IF. – BadCash

+0

@BadCash Это должно быть в идеале не так. Поскольку синхронизированный блок должен обеспечивать, чтобы оба кода были потокобезопасными. Таким образом, оба 'incrementAndGet()' и 'get()' не будут иметь никакого эффекта, поскольку только один поток будет иметь возможность выполнить внутри блока. Теперь, прибегая к безопасности потоков, возникает проблема за пределами синхронизированного блока 'int next = counter.incrementAndGet();' когда 2 потока увеличивают значение в одно и то же время, их локальная копия next будет иметь разные значения. Поэтому, рассматривая эту строку, оба кода не являются потокобезопасными. –

+0

Да, но в первой версии есть идентичный 'next = incrementAndGet(); if (next> MAX_VALUE) 'внутри синхронизированного блока, делая его потокобезопасным. Я предполагаю, что это тот же эффект, что и весь метод «synchronized» и только один IF-оператор, но с добавленной выгодой, что он не будет блокировать метод для других потоков для всех случаев, когда первый IF-оператор является ложным ... – BadCash

0

Think Оба метода логически связаны потоками. потому что в обоих методах используется синхронизация. и синхронизированный блок используется для обеспечения безопасности потоков.

0

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

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