[Java] Thread에 대한 공부
이번에는 Thread에 관한 내용을 중요한 포인트만을 가지고 정리해보고자 한다.
1. 개념정리
개인적으로 본인에게 와닿는 개념으로 정리해보았다.
- Process: 운영체제로부터 시스템 자원을 할당받는 작업의 단위, 실행 중인 프로그램
- Thread: 프로세스가 할당받은 자원을 이용하는 실행의 한 단위, 프로세스 내에서 실제로 작업을 수행하는 주체
2. Thread Life Cycle
- start(): 실행시 새로 생성된 Thread 객체가 Runnable로 이동해 실행 가능한 후보군이 되어 실제 실행을 대기한다.
- Scheduler: 스케쥴러가 각 스레드가 Running으로 이동시키거나 다시 Runnable로 이동시키면서 여러 스레드를 관리해준다. 일정한 규칙없이 여러 쓰레드들을 실행시켜준다.
- yield(): Running 상태의 스레드를 바로 Runnable로 이동시켜주는 메서드.
- Blocked: 여러 이유로 Running에서 실행 중지상태로 이동한다. I/O request나 synchronized 블럭이 존재시 다른 스레드들은 Blocked 상태로 이동하는데 요청이 종료될 때까지 Blocked 상태로 계속 있는다.
- sleep(밀리세컨드): 말그대로 입력해준 밀리세컨드 동안 스레드를 sleep상태로 재운다. 일정 시간동안 Blocked상태에 있는 것이다. 일정 시간이 지나고는 다시 Runnable상태로 이동하면서 정상으로 돌아온다.
- wait(), notify[All](): 일시적으로 스레드를 대기시키고 싶을 때 사용한다. notify() (혹은 notifyAll)이 호출되면 다시 Runnable상태로 이동
- Dead: run() 메서드가 정상적으로 종료되거나 stop()메서드를 이용하면 스레드가 완전히 종료되고 사라진다.
(일시정지상태는 정확하게 WAITING, TIMED-WAITING, BLOCKED 세 종류가 있다.
WAITING: wait(), join(), park() 메서드 등을 이용해 대기하고 있는 상태로 전환
TIMED-WAITING: wait(시간), join(시간), park(시간)을 이용해 최대 대기 시간을 정하고 대기상태로 만들 수 있다.
BLOCKED: monitor(일종의 제어권?)를 획득하기 위해 다른 스레드가 락을 해제하기를 기다리는 상태)
3. Thread 생성 방법
Thread 생성방법에는 크게 1) Runnable 인터페이스를 상속받아 사용하는 방법과 2) Thread 상속받아서 사용하는 방법 크게 두가지로 나뉜다.
1) Runnable 인터페이스 상속받는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
String threadName = Thread.currentThread().getName();
System.out.printf("%s : %d\n", threadName, i);
}
}
public static void main(String[] args) {
System.out.println("***********start***********");
RunnableDemo r1 = new RunnableDemo();
Thread t1 = new Thread(r1, "first thread");
RunnableDemo r2 = new RunnableDemo();
Thread t2 = new Thread(r2, "second thread");
System.out.println("************end************");
}
}
|
2) Thread 클래스 상속받는 경우
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
|
// Thread를 만드는 두 번째 방법
// Thread class는 이미 Runnable을 상속받고 있음
public class ThreadDemo extends Thread {
public ThreadDemo(String threadName) {
super(threadName);
}
public void run() {
for (int i = 0; i < 1000; i++) {
String threadName = Thread.currentThread().getName();
System.out.printf("%s : %d\n", threadName, i);
}
}
public static void main(String[] args) {
// 쓰레드 객체를 생성하는 방법
// 1. Thread 클래스를 상속 받는다.
// 2. run 메서드를 오버라이딩 한다.
// 3. 해당 객체(ThreadDemo)를 생성한다.
// 4. 해당 객체로 start 메서드를 호출한다.
System.out.println("***********start***********");
ThreadDemo t1 = new ThreadDemo("first thread");
// 여기서 멈추는 것이 아니라 아래로 그냥 내려감
ThreadDemo t2 = new ThreadDemo("second thread");
System.out.println("************end************");
// main부분의 맨마지막에 먼저 도달했다고
// 프로그램이 종료되진 않는다.
}
}
|
3. synchronized
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
45
46
47
|
public class SynchronizedDemo implements Runnable {
int x = 0;
int y = 0;
// synchronized는 같은 객체를 공유할 때만 적용
// 자기가 제어권을 획득했을 때 그때는 다른 객체에 제어권을 안넘김
// 한번 시작하면 제어권을 안 넘겨줌
@Override
public synchronized void run() {
for (int i = 0; i < 1000; i++) {
x++;
y++;
String threadName = Thread.currentThread().getName();
System.out.printf("x : %d , y : %d - %s\n", x, y, threadName);
}
}
public static void main(String[] args) {
SynchronizedDemo s1 = new SynchronizedDemo();
// #new Thread에 같은 객체를 넣을 경우
// instance variable을 서로 공유함 (x, y를 딱 1000번 돌림)
// #다른 객체를 넣을 경우
// 서로 다른 맴버변수를 사용함(각 객체당 1000개씩 2000번 돌림)
Thread t1 = new Thread(s1);
Thread t2 = new Thread(s1);
// 이런 식으로 원하는 시간 주기로 돌릴 수 있다.
while (true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 계속 5초마다 실행됨
new Thread(s1).start();
}
}
}
|
synchronized는 앞에서 접근한정자에서 언급만하고 지나간적이 있다. Thread에서 사용하는 것으로 같은 객체를 가지고 쓰레드를 여러개 만들어 실행시킬 경우에 적용한다. 같은 객체를 여러 스레드로 돌리면 그 안의 instance variable들을 공유하게 된다. 즉 위의 코드대로 실행시키면 t1, t2 스레드에 각각 x, y 최종값이 1,000씩 출력하는 것이 아니라 t1, t2스레드가 하나의 객체를 공유하기 때문에 최종값으로 x, y가 2,000이 출력된다.
여기서 synchronized 블럭을 적용하면 해당하는 메서드나 블럭이 실행되는 동안에는 다른 스레드에 제어권을 절대로 넘기지 않는다. 즉 같은 맴버변수를 공유한 채로 스레드를 여러번 돌려도 한 스레드가 실행될 동안에 제어권을 절대로 안넘겨주기에 이상한 값이 출력되거나 꼬일 일이 없다.
synchronized 사용법은 1) 메서드화 하거나 2) 블럭으로 만들어서 사용하면 된다. 위의 코드는 synchronized를 메서드화해서 사용한 것이다.
1
2
3
4
5
6
7
8
9
10
11
|
for (int i = 0; i < 1000; i++) {
synchronized (SynchronizedDemo.class) {
// 같은 객체를 공유하면 x++하고 중간에 제어권을 뺏길 수 있음
// x, y가 예상한 값이 아닌 이상한 값을 출력할 가능성 존재
// synchronized로 해결가능
x++;
y++;
String threadName = Thread.currentThread().getName();
System.out.printf("x : %d , y : %d - %s\n", x, y, threadName);
}
}
|
위의 코드는 synchronized 블럭으로 사용하는 방법을 담고 있다.
※정리
1. Thread 생성 방법은 Runnable 인터페이스 상속받는 방법, Thread 클래스 상속받는 방법 두가지가 있다.
2. synchronized는 같은 객체를 여러 스레드로 돌릴 때 발생할 수 있는 에러를 방지한다. (제어권을 획득한 스레드만이 실행할 수 있으며 다른 스레드는 Blocked 상태)
'Java' 카테고리의 다른 글
[Java] Static과 관련해서 더 자세히 알아보자 (0) | 2019.12.10 |
---|---|
[Java] IO 입출력(Stream)에 대한 공부 (0) | 2019.12.05 |
[Coding] 달력 출력하기 (Java) (0) | 2019.12.02 |
[Java] equals & hashCode - 두 method에 대한 공부 (0) | 2019.12.02 |
[Java] Access Modifier - 접근한정자에 대한 공부 (0) | 2019.12.02 |
댓글
댓글(Github)
다른 글
-
[Java] Static과 관련해서 더 자세히 알아보자
[Java] Static과 관련해서 더 자세히 알아보자
2019.12.10 -
[Java] IO 입출력(Stream)에 대한 공부
[Java] IO 입출력(Stream)에 대한 공부
2019.12.05 -
[Coding] 달력 출력하기 (Java)
[Coding] 달력 출력하기 (Java)
2019.12.02 -
[Java] equals & hashCode - 두 method에 대한 공부
[Java] equals & hashCode - 두 method에 대한 공부
2019.12.02