자바 스레드의 모순
자바 스레드는 프로그램을 병렬처리 흐름으로 할 수 있기 때문에 꼭 필요한 기능입니다.
그러나 스레드를 여러 개 사용할 때에는 주의해야 합니다.
그 이유는 여러개의 스레드에서 단일 필드에 동시에 접근하는 경우에 발생할 수 있습니다.
스레드 모순
예제 프로그램
아래와 같은 프로그램이 있습니다.
클래스 2개가 쓰레드로 돌면서 add 함수를 호출하여 tmp 값을 더하고 있습니다.
tmp 라는 변수는 이 2개의 스레드에서 들어오는 값을 누적하는 용도로 사용됩니다.
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
32
33
34
35
36
37
38
39
40
41
42
|
public class ThreadSyncTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
source sr = new source();
add ad1 = new add(1, sr);
add ad2 = new add(2, sr);
ad1.start();
ad2.start();
}
}
class add extends Thread {
private source sc ;
int id;
public add(int i, source sc) {
this.sc = sc;
this.id = i;
}
public void run () {
for(int i=0; i<3; i++) {
sc.add(id, 50);
}
}
}
class source {
private int sum = 0;
public void add ( int id, int a ) {
int tmp = sum ;
System.out.println ( "[" + id + "] 현재 값은 " + tmp + "입니다.");
tmp = tmp + a;
System.out.println ( "[" + id + "] 더한 값은 " + tmp + "입니다.");
sum = tmp;
}
}
|
cs |
결과는 어떻게 되었을까요?
제가 원하는 결과는 모든 수가 합산된 300을 원했으나, 하나의 스레드마다 150 씩의 값을 가지게 되었습니다.
심지어 스레드가 동작하는 순서에 따라 결과값도 달라지게 됩니다.
왜 이런일이 발생할까?
쓰레드의 흐름으로 생각해 봅니다.
쓰레드 1의 add 함수가 끝나기 전에, 쓰레드 2에서 add 함수를 호출한 경우입니다.
결과적으로 2번의 add 함수가 실행되었지만 sum 값은 50이 되었습니다.
이렇게 여러 개의 스레드가 공유 자원에 접근할 때 값의 모순이 발생할 수 있습니다.
쓰레드1 | 쓰레드2 | |
Step 1 | add ( ) 메소드 실행 | |
Step 2 | tmp = sum => tmp = 0 | |
Step 3 | tmp = tmp + 50 | |
Step 4 | add ( ) 메소드 실행 | |
Step 5 | tmp = sum => tmp = 0 | |
Step 6 | tmp = tmp + 50 | |
Step 7 | sum = 50 | |
Step 8 | sum = 50 |
synchronized 옵션
당연한 이야기 이지만, 위의 모순을 해결하기 위한 방안도 존재합니다.
함수 리턴타입 앞에 synchronized 를 붙이는 방법입니다.
이 옵션을 붙이게 되면, 메소드가 스레드에 의해 실행되는 동안, 다른 스레드는 이 메소드를 호출할 수 없습니다.
즉 synchronized 함수가 모두 끝날 때까지 다른 스레드는 기다리는 구조가 되는 것 입니다.
35번라인 add 함수 앞에 synchronized 옵션을 붙인 후 실행한 결과입니다.
2개의 스레드 순서가 얽히며 add 함수가 호출되었지만 항상 같은 결과 값을 보장받을 수 있습니다.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package Chapter15;
public class ThreadSyncTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
source sr = new source();
add ad1 = new add(1, sr);
add ad2 = new add(2, sr);
ad1.start();
ad2.start();
}
}
class add extends Thread {
private source sc ;
int id;
public add(int i, source sc) {
this.sc = sc;
this.id = i;
}
public void run () {
for(int i=0; i<3; i++) {
sc.add(id, 50);
}
}
}
class source {
private int sum = 0;
public synchronized void add ( int id, int a ) {
int tmp = sum ;
System.out.println ( "[" + id + "] 현재 값은 " + tmp + "입니다.");
tmp = tmp + a;
System.out.println ( "[" + id + "] 더한 값은 " + tmp + "입니다.");
sum = tmp;
}
}
|
cs |
'Computer Language > JAVA' 카테고리의 다른 글
[Java] 이클립스 The selection cannot be launched, and there are no recent launches 오류 (2) | 2021.03.31 |
---|---|
[JAVA] 자바 public static void main (String[] args) 의미 (8) | 2021.03.28 |
[JAVA] 자바 스레드 사용방법 (1) | 2021.03.25 |
[JAVA] 자바 예외처리 방법 try catch finally 문 (0) | 2021.03.17 |
[JAVA] 자바 임포트(import)란 (0) | 2021.03.16 |