728x90
반응형
public class SharedState {
@Test
public void sharedState(){
final ExecutorService executorService = Executors.newCachedThreadPool();
final SimpleCounter simpleCounter = new SimpleCounter();
executorService.execute(new CounterSetter(simpleCounter));
simpleCounter.setNumber(200);
assertEquals(200, simpleCounter.getNumber());
}
private static class CounterSetter implements Runnable{
private final SimpleCounter simpleCounter;
public CounterSetter(SimpleCounter simpleCounter) {
this.simpleCounter = simpleCounter;
}
@Override
public void run() {
while(true){
simpleCounter.setNumber(100);
}
}
}
public class SimpleCounter{
private int number = 0;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
}
위의 코드 내용은 다음과 같다.
하나의 쓰레드는 지속적으로 SimpleCounter의 number를 100으로 설정한다.
메인 쓰레드에서는 같은 SimpleCounter의 number를 200으로 설정한다.
해당 테스트는 실패할 수도 있고, 성공할 수도 있다.
이런일이 발생하는 이유는 JVM이 스레드가 실행될 순서를 정하기 때문이다.
여러개의 쓰레드가 데이터(객체)를 공유하는 상황이라면 우리는 하나의 쓰레드가 공유자원을 사용하고 있다면 해당 상태가 변경되지 않는다는 걸 확인하기 위해 공유 상태를 잠궈둬야 한다.
CounterSetter메서드가 메인 스레드의 실행을 방해하지 않게 하기 위해서는 아래와 같이 코드를 수정할 수 있다.
public class SharedState {
@Test
public void sharedState(){
final ExecutorService executorService = Executors.newCachedThreadPool();
final SimpleCounter simpleCounter = new SimpleCounter();
executorService.execute(new CounterSetter(simpleCounter));
synchronized (simpleCounter) {
simpleCounter.setNumber(200);
assertEquals(200, simpleCounter.getNumber());
}
}
private static class CounterSetter implements Runnable{
private final SimpleCounter simpleCounter;
public CounterSetter(SimpleCounter simpleCounter) {
this.simpleCounter = simpleCounter;
}
@Override
public void run() {
while(true){
synchronized (simpleCounter) {
simpleCounter.setNumber(100);
}
}
}
}
public class SimpleCounter{
private int number = 0;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
}
synchronized(Object) 키워드를 사용하면 한 번에 스레드 하나만 코드 블록 안에서 실행될 수 있게 된다. 동기화된 블록에 표시된 객체는 락으로 사용된다.
락을 사용하면 모든 읽기와 쓰기는 동기화된 코드 블록으로 둘러쌓인 simpleCounter 인스턴스로 인해 메인 스레드가 simpleCounter의 값을 업데이트하고 읽는 동안
synchronized (simpleCounter) {
simpleCounter.setNumber(200);
assertEquals(200, simpleCounter.getNumber());
}
CounterSetter는 simpleCounter의 값을 변경할 수 없게되어 테스트는 항상 통과한다.
물론 lock을 사용하면 성능상 문제가 발생하니 필요가 없다면 최대한 락을 해제하는 것이 좋다.
728x90
반응형
'그 외 ... (정리해야함) > 질문과 답변' 카테고리의 다른 글
템플릿 메서드 패턴은 어떻게 사용하는가? (0) | 2019.05.11 |
---|---|
Atomic 클래스는 무엇을 제공하는가? (0) | 2019.05.07 |
JVM에서 동작하는 실제 자바 코드를 작성할 때 생명주기란 무엇인가? (0) | 2019.05.07 |
JVM의 힙 크기는 어떻게 지정할 수 있는가? (0) | 2019.05.07 |
JVM 메모리는 어떻게 할당되는가? (0) | 2019.05.05 |