volatile은 멀티스레드에서 값들을 캐시하지않고 변경된 내용을 바로바로 가져올 수 있게 해준다.
오! 그렇다면 아무 곳에나 volatile만 넣어준다면 마법처럼 동기화가 되면서 공유상태를 사용할 수 있는 것일까?
아래 소스코드와 결과를 보면 그렇제 않은 것 같다.
package volatile_test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class VolatileTest1 { private volatile static int counter = 0; public static void main(String[] args) { Runnable r1 = () -> { System.out.println(counter); counter++;}; ExecutorService es = Executors.newCachedThreadPool(); for(int i = 0; i < 10; i++) { es.execute(r1); } es.shutdown(); // OUTPUT // 0 // 0 // 0 // 3 // 4 // 5 // 6 // 7 // 8 // 9 } }0이 3번이나 나왔다. 왜그럴까? 특이한 것은 결과적으로 +1이 잘 되었지만 조회가 잘 안되었다는 것인데 다시 한 번 해보자.
0 0 0 0 0 0 6 6 8 8전혀 다른 값이 나왔다. 그 말은 값이 달라졌다는 것이다. volatile을 사용한다고 동기화가 되는 것이 아니다. 읽기-수정-쓰기 가 동기화가 되야 하는 것이다. 그렇다면 volatile은 언제 써야 할까? 아래와 같은 경우를 보자.
package volatile_test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class VolatileTest2 { // 아래 내용을 보자. 과연 잘 돌아가는 경우가 얼마나 있을까? private static boolean flag = true; public static void main(String[] args) { Runnable r1 = () -> { while(flag) { // 여기에 막히게 된다. } System.out.println("bye"); }; Runnable r2 = () -> { System.out.println("flag false start"); flag = false;}; ExecutorService es = Executors.newCachedThreadPool(); for(int i = 0; i < 10; i++) { es.execute(r1); } es.execute(r2); es.shutdown(); } }여기서는 전혀 돌아가지는 않는다. 이것은 volatile의 부재일 수도 있지만, 컴파일러 마다 최적화를 하면서 결과값이 다르게 나올 수도 있다고 한다. 여튼 위 내용은 현재 flag를 false로 변경했지만 먹히지 않는다. 이때 volatile을 넣어보자.
package volatile_test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class VolatileTest3 { // 이럴때 volatile로 바꾸는 것만으로 효과가 있다. private volatile static boolean flag = true; public static void main(String[] args) { Runnable r1 = () -> { while(flag) { // System.out.println("hi"); } System.out.println("bye"); }; Runnable r2 = () -> { System.out.println("flag false start"); flag = false;}; ExecutorService es = Executors.newCachedThreadPool(); for(int i = 0; i < 10; i++) { es.execute(r1); } es.execute(r2); es.shutdown(); } }
댓글 없음:
댓글 쓰기