2015-04-20 2 views
1

В следующем коде ReentrantLock используется для предотвращения создания нечетных чисел с помощью метода next(). Но метод next() генерирует нечетные числа. Но если я изменю его на nextWithTry, он не генерирует нечетные числа. Может ли кто-нибудь объяснить причину этого?Состояние гонки в Java с ReentrantLock

class Generator{ 

    Lock l = new ReentrantLock(); 
    volatile int c = 0; 

    public int next(){ 

     l.lock(); 
     c++; c++; 
     l.unlock(); 

     return c; 
    } 

    public int nextWithTry(){//This method works fine... 

     try{ 
      l.lock(); 
      c++; c++; 
      return c; 
     }finally{ 
      l.unlock(); 
     } 
    } 
} 

class W implements Runnable{ 

    private Generator r; 

    public W(Generator r){ 
     this.r = r; 
    } 

    @Override 
    public void run() { 

     int x; 

     while(true){ 
      if(((x = r.next()) % 2) != 0){ 
       System.out.println(x + " odd number Found"); 
       break; 
      } 
     } 
    } 
} 

public class Testing { 

    public static void main(String[] args) { 

     Generator r = new Generator(); 

     W w1 = new W(r); 

     new Thread(w1).start(); 
     new Thread(w1).start(); 
    } 
} 
+0

Я использую java версию 1.7.0_51 с eclipse JUNO. Я пробовал без изменчивости. То же самое происходит. – maamaa

+0

@BoristheSpider Я считаю, что у вас есть правильный ответ. Проблема с упорядочением, но это не в оптимизации jit, а в самом коде, как вы указываете. –

ответ

3

Что происходит, когда что-то еще увеличивает c между unlock и return?

public int next(){ 
    //lock, exclusive access 
    l.lock(); 
    //increment, all good 
    c++; c++; 
    //unlock, another thread can access 
    l.unlock(); 

    //any number of other threads call `next` and can acquire the lock 

    //return some random value 
    return c; 
} 

При использовании finally, то lock освобождается только после того, значения c быть возвращены уже в стеке:

public int nextWithTry() { 
    try { 
     //lock, exclusive access 
     l.lock(); 
     //increment, all good 
     c++; c++; 
     //place the value of `c` to be returned on the stack (java passes by value) 
     return c; 
    } finally { 
     //unlock _after_ the return has been copied 
     l.unlock(); 
    } 
} 

В самом деле, the documentation непосредственно рекомендует использовать try..finally:

В большинстве случаев следует использовать следующую идиому:

Lock l = ...; 
l.lock(); 
try { 
    // access the resource protected by this lock 
} finally { 
    l.unlock(); 
} 

Это позволяет избежать проблем, как это, а также более серьезные из них, где Exception вызывает Lock не будет разблокирован.

+0

Или вы можете сохранить значение 'c' в локальной переменной перед разблокировкой, а затем вернуть это значение, если хотите по какой-то причине избежать блокировки' try'. –

+0

@stvcisco да, обязательно. Но рекомендуется [документация] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html) использовать 'try ... finally' для избегайте проблем, когда блокировки не выпускаются. –

+0

спасибо .. наконец-то получил это ... – maamaa

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