2012-03-23 3 views
1

У меня есть метод getNewA(), который должен блокироваться до тех пор, пока какой-либо другой поток не вызовет setA (x). Правильно ли использовать CountDownLatch? Я заметил, что есть гонка данных, в которой есть вероятность, что после того, как gate.await() разблокирует другой поток, вызовет setA (x), используя старую защелку, поэтому можно было бы пропустить значение. Я думал о синхронизации getNewA(), но разве это не привело бы к тупиковой ситуации? Любые предложения о том, как подойти к этому?CountDownLatch Synchronization

package test; 

import java.util.concurrent.CountDownLatch; 

public class A { 

    private int a; 
    private CountDownLatch gate; 

    public A(int a) { 
     a = 1; 
     gate = new CountDownLatch(1); 
    } 

    A getNewA() throws InterruptedException { // wait for new a... 
     gate.await(); 

     gate = new CountDownLatch(1); 
     return this; 
    } 

    public synchronized int getA() { 
     return a; 
    } 

    public synchronized void setA(int a) {  
     gate.countDown(); 
     this.a = a; 
    } 
} 
+0

Если другой поток вызывает 'setA' * перед *' getNewA() ', что это значит? Может ли семафор быть более полезным для вас? –

+0

Если setA находится перед getNewA(). Поток, который вызывает getNewA(), вернет экземпляр без блокировки – xxtommoxx

+0

И если 'setA()' вызывается дважды до вызова 'getNewA()', что же происходит? Могут ли два вызова 'getNewA()' проходить без блокировки? Если два потока ожидают 'getNewA()', и приходит один вызов 'setA()', должны ли они быть разблокированы или только один? Вам действительно нужно подробно описать вашу ситуацию :) –

ответ

2

Использовать Phaser. Вы можете использовать его так же, как хотите, без необходимости создавать новый экземпляр вашего барьера.

public class A { 

    private int a 
    private final Phaser phaser = new Phaser(1); 


    public A(int a) { 
     a = 1; 
    } 


    A getNewA() throws InterruptedException { // wait for new a... 
     phaser.awaitAdvance(phaser.getPhase()); 
     return this; 
    } 

    public synchronized int getA() { 
     return percent; 
    } 

    public synchronized void setA(int a) {  
     this.a = a 
     phaser.arrive(); 
    } 
} 

Каждый раз, когда setA называется она будет увеличиваться в новую фазу и phaser.awaitAdvance(phaser.getPhase()) вернется из. На этом этапе новая фаза будет равна phaser.getPhase()+1

Примечание: это требует Java 7.

+0

Любые альтернативы, если im использует java 6? – xxtommoxx

+0

Не будет работать и SynchronousQueue? – lhballoti

+0

@xxtommoxx Я думал о некоторых обычаях с FutureTask, но мне не нравится это, чтобы опубликовать его. Я могу работать лучше. –

0

Вы можете использовать Semahore:

package test; 

import java.util.concurrent.Semaphore; 

public class A { 

    private int a 
    private Semaphore semaphore = new Semaphore(0); 


    public A(int a) { 
     a = 1;    
    } 


    A getNewA() throws InterruptedException { // wait for new a... 
     semaphore.acquire(); 
     return this; 
    } 

    public synchronized int getA() { 
     return percent; 
    } 

    public synchronized void setA(int a) {     
     this.a = a; 
     semaphore.release(); 
    } 
} 

Если семафор 0 позволяет слева и два потока вызовите getA один за другим, оба будут блокироваться, и только они будут детерминистически выбраны для пробуждения при вызове setA.

Если setA вызывается дважды в последовательности, это позволит двум потокам вызвать getA в следующий раз, что не может быть то, что вы хотите, так как оба они получат ту же ссылку на this.

1

Альтернативой является обработка синхронизации самостоятельно. Я думаю, что вы хотите, чтобы getA возвращал значение, установленное после входящего потока. Вы можете использовать это как критерии wait().

public class A { 

    private int a; 
    private long updateCount = 0; 

    private final Object lock = new Object(); 

    public A getNewA() throws InterruptedException { // wait for new a... 
     synchronized(lock) { 
      long currentCount = updateCount ; 
      while (currentCount == updateCont) {//assumes never decrementing 
       lock.wait(); 
      } 
      return this; 
     } 
    } 

    public int getA() { 
     synchronized(lock) { 
      return a; 
     } 
    } 

    public void setA(int a) {  
     synchronized(lock) { 
      this.a = a; 
      updateCount++; 
      lock.notifyAll(); 
     } 
    } 
} 

Редактировать: Состояние гонки возможно, как упоминал ТомАндерсон. Спасибо

+1

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

+0

@TomAnderson Это справедливая точка. Я переключусь на AtomicLong. –

+2

Не нужно быть атомарным; все обращения защищены синхронизацией на 'lock'. –