Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Thymeleaf
- AOP
- 스프링 핵심 기능
- pointcut
- Exception
- JPQL
- SpringBoot
- Servlet
- 스프링 핵심 원리
- springdatajpa
- JDBC
- 스프링
- Spring Boot
- spring
- 인프런
- 자바
- db
- Greedy
- QueryDSL
- Android
- Proxy
- transaction
- 그리디
- 알고리즘
- kotlin
- jpa
- java
- 김영한
- http
- 백준
Archives
- Today
- Total
개발자되기 프로젝트
동시성 문제 - 예제 코드 본문
1. Test에서 lombok 사용
- dependancies에 추가.
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
2. FieldService
- FieldService는 logic() 메서드를 제공한다.
- name을 받으면 name sotre에 저장 후 값을 반환한다.
- name sotre에 저장하는데 1초가 걸린다고 가정.
@Slf4j
public class FieldService {
private String nameStore;
public String logic(String name){
log.info("저장 name={} --> nameStore={}", name, nameStore);
nameStore = name;
sleep(1000);
log.info("조회 nameStore={}", nameStore);
return nameStore;
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3. FieldServiceTest - 순차적 실행.
- 여러 Thread가 경쟁하는 상황을 만들기 전에 Thread가 순차적으로 실행되는 상황을 보자.
- threadA실행 후 2초의 sleep을 두어 threadA가 완전히 수행할 시간을 둠.
- 참고) Thread 생성 방법
- Ruunable 인터페이스의 run() 구현.
- Thread는 Thread 클래스를 상속 받거나, Runnable 인터페이스를 구현하거나,
- Runnable 인터페이스 구현 후 Thread에 주입하는 방식으로 사용 가능.
https://bsh-developer.tistory.com/584
- threadA, threadB가 순차적으로 실행 될 수 있도록 조건을 부여함.
@Slf4j
public class FieldServiceTest {
private FieldService fieldService = new FieldService();
@Test
void field(){
log.info("main start");
/*
Runnable runnable= new Runnable() {
@Override
public void run() {
}
}*/
//Ruunable 인터페이스의 run() 구현.
//Thread는 Thread 클래스를 상속 받거나, Runnable 인터페이스를 구현하거나,
//Runnable 인터페이스 구현 후 Thread에 주입하는 방식으로 사용 가능.
Runnable userA = () -> {
fieldService.logic("userA");
};
Runnable userB = () -> {
fieldService.logic("userB");
};
Thread threadA = new Thread(userA);
threadA.setName("thread-A");
Thread threadB = new Thread(userB);
threadA.setName("thread-B");
threadA.start();
sleep(2000); //동시성 문제 발생 안하는 조건.
threadB.start();
sleep(2000); //메인 Thread 종료 대기
log.info("main exit");
}
private void sleep(int millis){
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
[main] - main start
[thread-A] - 저장 name=userA --> nameStore=null
[thread-A] - 조회 nameStore=userA
[Thread-B] - 저장 name=userB --> nameStore=userA
[Thread-B] - 조회 nameStore=userB
[main] - main exit
- 실행 결과
- Thread-A 는 userA 를 nameStore 에 저장
- Thread-A 는 userA 를 nameStore 에서 조회
- Thread-B 는 userB 를 nameStore 에 저장
- Thread-B 는 userB 를 nameStore 에서 조회
4. FieldServiceTest - 동시성 문제 발생.
- 여러 Thread가 경쟁하는 상황을 만들자.
- threadA실행 후 0.1초의 sleep을 두어 threadA가 완전히 수행되기 전에 theadB가 실행되도록 함.
@Slf4j
public class FieldServiceTest {
private FieldService fieldService = new FieldService();
@Test
void field(){
log.info("main start");
/*
Runnable runnable= new Runnable() {
@Override
public void run() {
}
}*/
//Ruunable 인터페이스의 run() 구현.
//Thread는 Thread 클래스를 상속 받거나, Runnable 인터페이스를 구현하거나,
//Runnable 인터페이스 구현 후 Thread에 주입하는 방식으로 사용 가능.
Runnable userA = () -> {
fieldService.logic("userA");
};
Runnable userB = () -> {
fieldService.logic("userB");
};
Thread threadA = new Thread(userA);
threadA.setName("thread-A");
Thread threadB = new Thread(userB);
threadA.setName("thread-B");
threadA.start();
//sleep(2000); //동시성 문제 발생 안하는 조건.
sleep(100); //동시성 문제 발생하는 조건.
threadB.start();
sleep(2000); //메인 Thread 종료 대기
log.info("main exit");
}
private void sleep(int millis){
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- threadA가 조회하기 전에 threaB가 userB를 저장해버림.ㅋㅋㅋ
- 그래서 threadA는 userB를 조회하게됨.
[main] - main start
[thread-A] -저장 name=userA --> nameStore=null
[thread-B] -저장 name=userB --> nameStore=userA
[thread-A] -조회 nameStore=userB
[thread-B] -조회 nameStore=userB
[main] - main exit
- theadA가 nameStore에 userA 저장
- +0.1초 후 theadB가 nameStore에 userB 저장
- +1.0초 후 theadA가 nameStore에 저장된 정보(userB) 조회
- +1.1초 후 theadB가 nameStore에 저장된 정보(userB) 조회
5. 동시성 문제
- 결과적으로 Thread-A 입장에서는 저장한 데이터와 조회한 데이터가 다른 문제가 발생한다.
- 여러 쓰레드가 동시에 같은 인스턴스의 필드 값을 변경하면서 발생하는 문제를 동시성 문제라 한다.
- 이런 동시성 문제는 여러 쓰레드가 같은 인스턴스의 필드에 접근해야 하기 때문에
- 트래픽이 적은 상황에서는 확률상 잘 나타나지 않고, 트래픽이 점점 많아질 수 록 자주 발생한다.
- 특히 스프링 빈 처럼 싱글톤 객체의 필드를 변경하며 사용할 때 이러한 동시성 문제를 조심해야 한다
6. 참고
- 이런 동시성 문제는 지역 변수에서는 발생하지 않는다.
- 지역 변수는 쓰레드마다 각각 다른 메모리 영역이할당된다.
- 동시성 문제가 발생하는 곳은 같은 인스턴스의 필드(주로 싱글톤에서 자주 발생),
- 또는 static 같은 공용 필드에 접근할 때 발생한다.
- 동시성 문제는 값을 읽기만 하면 발생하지 않는다.
- 어디선가 값을 변경하기 때문에 발생한다.
7. GitHub : 211122 Concurrency Issue
'인프런 > [인프런] 스프링 핵심 원리 - 고급' 카테고리의 다른 글
ThreadLocal - 예제 (0) | 2021.11.22 |
---|---|
Thread Local - 소개 (0) | 2021.11.22 |
필드 동기화 - 동시성 문제 (0) | 2021.11.20 |
필드 동기화 - 적용 (0) | 2021.11.20 |
필드 동기화 - 개발 (0) | 2021.11.20 |
Comments