일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 리액트
- REACT
- java
- grafana
- Linux
- IntelliJ
- Spring Boot
- JavaScript
- 리눅스
- Docker
- gradle
- subnetmask
- jenkins github 연동
- grpc
- error
- jenkins install
- Jenkins Pipeline
- jenkins maven
- docker network
- jenkins jdk
- jenkins 설치
- jpa
- Jenkins
- MongoDB
- vue.js
- CI/CD
- spring
- MySQL
- nginx
- jenkins github
- Today
- Total
뭐든 즐기면서 ;)
Thread / Java Thread 본문
아래는 Thread의 문제점을 확인해 보는 코드입니다.
'ctn' 변수를 가지며, 이 변수를 1씩 증가 시키는 'getNextCounter' 함수를 포함하고 있는 class Counter를 작성합니다. 그리고 Counter 인스턴스를 주입받는 Thread를 1,2,3 생성합니다.
Thread 3개에 공유하는 데이터 Counter를 주입시키고, ctn을 for문으로 증가시킵니다.
여기서 예상되는 이상적인 결론은 ctn은 총 3만번 증가하여 최종 값은 30001이 되는 것입니다. 그러나 아래 코드를 실행시켜 보면, 15962, 14692 등 예상과는 다르게 찍힙니다. 그 이유는 아래 코드를 먼저 본 후 설명하겠습니다.
public static class Counter {
private int cnt;
public Counter() {
this.cnt = 0;
}
public /*synchronized*/ int getNextCounter() {
this.cnt = this.cnt + 1;
return this.cnt;
}
}
public static class MyThread extends Thread {
public Counter counter;
public int primitiveCounter;
public MyThread(Counter counter, int primitiveCounter) {
this.counter = counter;
this.primitiveCounter = primitiveCounter;
}
public void run() {
for (int i =0; i<10000; ++i) {
counter.getNextCounter();
primitiveCounter++;
}
}
}
public static void main(String[] args) {
Counter counter = new Counter();
int primitiveCounter = 0;
MyThread thread1 = new MyThread(counter, primitiveCounter);
MyThread thread2 = new MyThread(counter, primitiveCounter);
MyThread thread3 = new MyThread(counter, primitiveCounter);
thread1.start();
thread2.start();
thread3.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(thread1.counter.getNextCounter());
System.out.println(thread2.counter.getNextCounter());
System.out.println(thread1.primitiveCounter);
System.out.println(thread2.primitiveCounter);
}
'getNextCounter'함수 내에서 this.cnt = this.cnt + 1; 이 코드 한 줄은 사실상 CPU에 의해 3단계의 처리 단계를 거칩니다.
a. cnt 0을 메모리에서 레지스터로 불러옴.
b. 레지스터의 cnt를 +1한 후 레지스터에 다시 저장.
c. cnt를 레지스터에서 메모리로 덮어씌움.
이렇게 3단계를 거치게 되며, Thread 3개의 순서는 아래와 같이 섞일 수가 있습니다.
* 순서는 위 코드를 실행할 때마다 OS에 의해 랜덤으로 흘러갑니다.
T1 a) cnt 0를 메모리에서 레지스터로 불러옴
T1 b) 레지스터의 cnt에서 +1을 한 후 레지스터에 다시 저장
T2 a) cnt 0를 메모리에서 레지스터로 불러옴
T1 c) cnt를 레지스터에서 메모리로 덮어씌움 cnt => 1
T2 b) 레지스터의 cnt에서 +1을 한 후 레지스터에 다시 저장
T2 c) cnt를 레지스터에서 메모리로 덮어씌움 => 1
공유되는 cnt 데이터는 위와 같은 단계를 거쳐 30001이 아닌 랜덤된 숫자가 결과로 보여지게 됩니다.
이를 해결하기 위한 방법으로 자바에서는 'synchronized' 키워드를 사용합니다.
*Thread는 Heap영역의 데이터만 공유하고, Stack영역의 데이터는 공유하지 않습니다.
'IT정리' 카테고리의 다른 글
[이론] REST API / GraphQL (0) | 2024.12.31 |
---|---|
Gradle 로컬 저장소 라이브러리 추가 (0) | 2023.11.13 |
Error : org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:2.6.1:clean (default-clean) (0) | 2022.12.22 |
일급 객체(First Class Object) (0) | 2022.12.16 |
LRU(Least Recently Used) (0) | 2022.04.30 |