그래서 Brian Goetz의 JCIP을 읽고 volatile
동작을 실험하기 위해 다음 코드를 작성했습니다. 휘발성이 예상대로 작동하지 않습니다.
public class StatefulObject {
private static final int NUMBER_OF_THREADS = 10;
private volatile State state;
public StatefulObject() {
state = new State();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public static class State {
private volatile AtomicInteger counter;
public State() {
counter = new AtomicInteger();
}
public AtomicInteger getCounter() {
return counter;
}
public void setCounter(AtomicInteger counter) {
this.counter = counter;
}
}
public static void main(String[] args) throws InterruptedException {
StatefulObject object = new StatefulObject();
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
AtomicInteger oldCounter = new AtomicInteger();
AtomicInteger newCounter = new AtomicInteger();
object.getState().setCounter(oldCounter);
ConcurrentMap<Integer, Long> lastSeen = new ConcurrentHashMap<>();
ConcurrentMap<Integer, Long> firstSeen = new ConcurrentHashMap<>();
lastSeen.put(oldCounter.hashCode(), 0L);
firstSeen.put(newCounter.hashCode(), Long.MAX_VALUE);
List<Future> futures = IntStream.range(0, NUMBER_OF_THREADS)
.mapToObj(num -> executorService.submit(() -> {
for (int i = 0; i < 1000; i++) {
object.getState().getCounter().incrementAndGet();
lastSeen.computeIfPresent(object.getState().getCounter().hashCode(), (key, oldValue) -> Math.max(oldValue, System.nanoTime()));
firstSeen.computeIfPresent(object.getState().getCounter().hashCode(), (key, oldValue) -> Math.min(oldValue, System.nanoTime()));
}
})).collect(Collectors.toList());
executorService.shutdown();
object.getState().setCounter(newCounter);
futures.forEach(future -> {
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
System.out.printf("Counter: %s\n", object.getState().getCounter().get());
long lastSeenOld = lastSeen.get(oldCounter.hashCode());
long firstSeenNew = firstSeen.get(newCounter.hashCode());
System.out.printf("Last seen old counter: %s\n", lastSeenOld);
System.out.printf("First seen new counter: %s\n", firstSeenNew);
System.out.printf("Old was seen after the new: %s\n", lastSeenOld > firstSeenNew);
System.out.printf("Old was seen %s nanoseconds after the new\n", lastSeenOld - firstSeenNew);
}
}
그래서 나는
newCounter
가 항상 먼저
oldCounter
(나는 어느 것도 부실 카운터를 참조하지 않도록 모든 스레드 업데이트를 통지 할 것으로 예상) 마지막으로 목격 된 후에 만 볼 것으로 기대하고있다. 이 동작을 관찰하기 위해 두 개의 맵을 사용합니다. 그러나 놀랍게도 저는 계속 이런 식으로 결과를 얻습니다 :
Counter: 9917
Last seen old counter: 695372684800871
First seen new counter: 695372684441226
Old was seen after the update: true
Old was seen 359645 nanoseconds after the new
내가 잘못한 점을 설명해 주시겠습니까?
미리 감사드립니다.
:
난 그냥 위에서 언급 한 문제를 생략하는 코드에서 몇 가지를 변경했습니다. – nos
@nos 따라서 휘발성 카운터 필드를 업데이트 한 후에도 이전 카운터를 참조하지 않으므로 모든 스레드에서 업데이트를 볼 것으로 예상됩니다. – alxg2112