-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FIX: Change syncronization tool in getVersion(). #647
Conversation
b00bd29
to
5a25e71
Compare
|
기존 로직은 아래와 같습니다. synchronized (VERSION) {
// 내부에서 VERSION의 참조값 변경
} 현재 로직에서 Thread A가 VERSION을 처음으로 변경하려 할 때는 물론 스레드 B의 수행 로직은 블럭 내부의 다만 Atomic을 쓰면 기존에 비해 |
@@ -185,7 +185,7 @@ | |||
*/ | |||
public class ArcusClient extends FrontCacheMemcachedClient implements ArcusClientIF { | |||
|
|||
private static String VERSION = "INIT"; | |||
private static AtomicReference<String> VERSION = new AtomicReference<String>("INIT"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 곳에서 VERSION의 참조를 바꾸지 않으므로 final로 선언합시다.
if (!VERSION.equals("INIT")) { | ||
return VERSION; | ||
if (!VERSION.get().equals("INIT")) { | ||
return VERSION.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2회의 get() 사이에 값이 바뀔 수 있으므로 지역변수에 VERSION.get()을 담아두고 if문과 return문에서 사용합시다.
5a25e71
to
8820c50
Compare
따라서, 이 경우는 synchronized block 방식이 낫다고 봅니다. |
|
8820c50
to
2e47c88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 완료
@@ -4276,7 +4276,7 @@ public static String getVersion() { | |||
if (!VERSION.equals("INIT")) { | |||
return VERSION; | |||
} | |||
synchronized (VERSION) { | |||
synchronized ("INIT") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
동시 접근을 순서 접근으로 하려는 대상이 VERSION 객체이며, "INIT" 값은 아님.
따라서, 기존 코드가 논리적으로 이해하기 나음.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기존 코드에서 2개의 Thread가 getVersion() 메소드를 호출했을 때 다음과 같이 동작합니다.
- 1번 Thread가
VERSION.equals("INIT") == true
이므로 return 하지 않은 상태 - 2번 Thread로 Context Switch. 아직은
VERSION.equals("INIT") == true
이므로 return 하지 않은 상태 - 1번 Thread로 Context Switch. 1번 Thread는 VERSION 객체의 Lock을 획득합니다. VERSION 객체의 주소 값이 0xAAAAAA라고 가정합니다. 1번 Thread가 0xAAAAAA의 주소로 잡은 Lock을 획득한 채로 VERSION 객체의 값 변경. 변경된 VERSION 값의 주소 값을 0xBBBBBB라고 가정합니다.
- 2번 Thread로 Context Switch. 2번 Thread는 변경된 VERSION 객체의 Lock, 즉 0xBBBBBB의 주소로 Lock을 획득합니다. 이 상태로, 변경해도 그대로겠지만, 다시 VERSION 객체의 값 변경
최대 2개의 Thread에 의해 VERSION 설정 로직이 돌아갈 수 있습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2번 Thread로 Context Switch. 2번 Thread는 변경된 VERSION 객체의 Lock, 즉 0xBBBBBB의 주소로 Lock을 획득합니다. 이 상태로, 변경해도 그대로겠지만, 다시 VERSION 객체의 값 변경
0xBBBBBB의 참조를 가진다는 것이
이미 VERSION 변수의 값은 "INIT"이 아니기 때문에
version 정보를 얻어오는 로직은 수행되지 않는게 아닐까요?
2번 쓰레드의 경우 critical section에 진입해서
if (VERSION.equals("INIT"))
를 수행하고
조건을 만족하지 않아 그대로 VERSION을 리턴합니다.
"INIT" 이라는 값 자체를 아래와 같이 전역 상수화 시키면
INIT이라는 인스턴스를 통해 critical section에
접근 하려는 스레드들을 제어하려는
의미가 잘 드러난다고 생각합니다.
private static final String INIT = "INIT";
private static String VERSION = INIT;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@uhm0311 @brido4125
synchronized (VERSION)
과 같이 특정 변수 명을 주는 경우의 내부 동작에 대해 잘 몰랐던 것 같습니다.
지적한 문제를 해결하기 위해,
synchronized (this)
와 같이 this 변수를 사용하거나- 별도의
versionLock = new Object();
객체를 만들어 synchronize하는 것이 좋겠습니다.
2e47c88
to
7a3f425
Compare
아래와 같이 this를 사용하는 경우는 synchronized (this) {
// ...
} static 객체를 하나 만들어서 인수로 사용하도록 하겠습니다. |
7a3f425
to
dcaeabf
Compare
@uhm0311 @oliviarla |
Motivation
기존의 getVersion에서 동시성 이슈 해결은 synchronized 블럭을 통해 진행한다.
이는 쓰레드의 blocking을 야기해 성능의 문제가 존재한다.
또한 현재 synchronized 블럭의 인자인
VERSION
은synchronized 블럭 내에서 참조가 변경될 수 있다.
즉, threadA가 lock을 걸고 내부 로직에서 참조를 변경하면
threadB가 lock을 거는 참조 대상이 달라져
동기화가 제대로 이루어지지 않을 가능성이 존재한다.
변경지점
이러한 문제를 AtomicReference를 통해 해결합니다.
기존과 동일하게 버전 파싱 중 예외 발생 시는 "NONE"을 대입합니다.