2010-06-02 13 views
7

W jaki sposób używać AtomicInteger do ograniczonego generowania sekwencji, powiedzmy, że numer sekwencji musi wynosić od 1 do 60. Gdy sekwencja osiągnie 60, musi zacząć od nowa 1. Napisałem ten kod, choć nie do końca pewny czy to jest wątek bezpieczny, czy nie?AtomicInteger do generowania ograniczonej sekwencji

public int getNextValue() 
{ 
int v; 
do 
{ 
    v = val.get(); 
    if (v == 60) 
    { 
    val.set(1); 
    } 
} 
    while (!val.compareAndSet(v , v + 1)); 
    return v + 1; 
    } 

Odpowiedz

14

Można zrobić

return val.getAndIncrement() % 60; 

Jeśli nie jesteś zaniepokojony z przekroczeniem max wartość całkowitą (2147483647). Jeśli to jest problemem, można rzucić okiem na realizację getAndIncrement:

public final int getAndIncrement() { 
    for (;;) { 
     int current = get(); 
     int next = current + 1; 
     if (compareAndSet(current, next)) 
      return current; 
    } 
} 

Wszystko trzeba zmienić to linia do czegoś podobnego int next...:

int next = (current + 1) % 60; 

Ups. Ta pętla przechodzi przez 0-> 59. Potrzebujesz 1-> 60, więc dodaj jeden do wartości zwracanej, aby uzyskać pożądany wynik.

+0

+1. To naprawdę pomocne – satish

+0

Jeśli znajdziesz naprawdę pomocną odpowiedź, kliknij znak, aby ją zaakceptować. – naiad

+1

Co za dziwna implementacja. Zasadniczo mówi "dodaj 1, jeśli jeszcze się nie zmieniło, w przeciwnym razie próbuj dalej". Czy teoretycznie nie może to doprowadzić do nieskończonej pętli? –

0

Nie, to nie jest bezpieczny wątku - nie należy zadzwonić set wewnątrz cyklu:

int value, next; 
do { 
    value = val.get(); 
    next = (value == 60) ? 1 : (value + 1); 
} while (!val.compareAndSet(value, next); 
return next; 
1

Jeśli się metoda synchronized to będzie threadsafe dopóki val są dostępne nigdzie indziej. Podejście to jest jednak nieco kłopotliwe, to bym go przepisać następująco:

public synchronized int getNextValue() { 
    val.compareAndSet(60, 0); // Set to 0 if current value is 60. 
    return val.incrementAndGet(); 
} 

Daje 1 do 60 z powrotem włącznie. Jeśli rzeczywiście potrzebujesz 1 do 59, a następnie zastąp 60 przez 59.

0

Czy istnieje jakiś szczególny powód, aby używać tutaj AtomicInteger, a nie tylko prostej zsynchronizowanej metody?

Jak o coś prostego jak następuje:

private int val=1; 

public synchronized int getNextValue() { 
int v=val; 
val = (val==60) ? 1 : (val+1); 
return v; 
} 
+0

p.s. nic przeciwko AtomicIntegers, ale myślę, że zawsze dobrze jest robić rzeczy tak proste, jak to możliwe, w interesie długoterminowej konserwacji .... – mikera

+0

Dzięki za odpowiedź. Szukałem algorytmu non-blocking z algorytmu Blocking. – satish

+0

AtomicInteger jest znacznie szybszy niż synchronizacja, jeśli istnieje wiele niezgodności (do 10 czynników). – starblue

0

szybką odpowiedź, nie wątku bezpieczne. Test i zestaw muszą być atomowe, chyba że zsynchronizujesz całą metodę. Zauważ, że val.get() i test v nie są atomowe. Jeśli wątek zostanie uzyskany po v = val.get(), otrzymasz dwa połączenia o tym samym numerze kolejnym.

Ponadto, jeśli metoda compareAndSet ulegnie awarii, nigdy nie zmieni się wartości, będzie to nieskończona pętla.

AtomicInteger ma wywołanie getAndIncrement() . To da ci czystą wartość do zwrotu.

Walcowanie jest nieco trudniejsze. Jednym z rozwiązań jest zmodyfikowanie wartości zwracanej. Coś takiego:

int v = val.getAndIncrement(); 
return (v % 60) + 1; 

Ponieważ każdy wątek ma lokalną kopię v, możemy bezpiecznie wykonać kilka obliczeń i zwrócić wartość. Jest jeden punkt skupienia, jeśli pojawi się przepełnienie. W zależności od tego, jak często generujesz numer sekwencyjny, może to być problem.

+0

Częstotliwość generowania jest dość wysoka, prawdopodobnie co 50 ms. – satish

+0

Przy 20 połączeniach na sekundę (wywołanie co 50ms) można uzyskać rollover po około 3,4 roku. Prawdopodobnie nie jest tak źle, w zależności od twojej aplikacji. Jeśli to będzie problem, zsynchronizowany blok jest prawdopodobnie najłatwiejszym rozwiązaniem. Upewnij się, że ten kod jest prawdziwym wąskim gardłem, zanim oszalejesz, optymalizując go. Podejrzewam, że są niższe owoce wiszące niż zadanie, które działa co 50ms. –

3

Możesz to zrobić w jednym wierszu, używając Java 8.

AtomicInteger counter = new AtomicInteger(); 

public int getNextValue() { 
    return counter.updateAndGet(n -> (n >= 60) ? 1 : n + 1); 
} 
Powiązane problemy