Dostęp do licznika wykonań pętli powinien być w tym przypadku synchronizowany zarówno dla odczytów, jak i zapisów. W tym celu możesz użyć ReentrantLocka.
Przykład:
import java.util.concurrent.locks.ReentrantLock;
public class Sample implements Runnable {
// nasz obiekt do synchronizacji dostepu do maxIerations/worksSoFar
private static ReentrantLock lock = new ReentrantLock();
// max. ilosc wywolan petli
private static Integer maxIterations = 0;
// tyle razy wykonano petle
private static Integer workSoFar = 0;
@Override
public void run() {
// liczbe wykonan jaka zaobserwowal watek -> "consistent read"
long workSoFarSynchRead = 0;
// tyle razy petla wykonana przez biezacy watek
int workByCurrentThread = 0;
boolean done = false;
long sleepTime = 0;
System.out.printf("%s :: Thread started\n", Thread.currentThread());
while (!done) {
// staramy sie zrobic bardzo krotka sekcje krytyczna
lock.lock();
if (workSoFar < maxIterations) {
workSoFar++;
workSoFarSynchRead = workSoFar;
} else {
done = true;
}
lock.unlock();
// logika do wykonania
if (!done) {
try {
workByCurrentThread++;
sleepTime = (long) (Math.random() * 500);
System.out.printf("%s :: #AllIterations=%d, #IterationsByThread=%d. Working for next [%d] ms\n", Thread.currentThread(), workSoFarSynchRead, workByCurrentThread, sleepTime);
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
// ignore
}
}
}
System.out.printf("%s :: Thread finished\n", Thread.currentThread());
}
public static void main(String[] args) {
// tyle watkow bedzie pracowac
int workersCount = 5;
// tyle iteracji petli chcemy
maxIterations = 20;
// uruchamiamy watki
for (int i = 0; i < workersCount; i++) {
new Thread(new Sample()).start();
}
}
}
Ćwiczenie badawcze: co się stanie jeśli wątek zostanie przerwany między lock() i unlock() ?