=================================
=================================
=================================
출처: http://jangsalt.tistory.com/22
[Java] Thread의 정지
=================================
=================================
=================================
출처: http://gangzzang.tistory.com/75
Thread API : http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
Thread Group API : http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadGroup.html
1. 프로세스와 쓰레드
- 프로세스(Process) : 실행 중인 프로그램(Program), 자원(resources)과 쓰레드로 구성, 공장에 비유된다.
- 쓰레드(Thread) : 프로세스 내에서 실제 작업을 수행한다, 모든 프로세스는 하나 이상의 쓰레드를 가지고 있다, 일꾼에 비유된다.
- 싱글쓰레드 프로세스 = 자원 + thread
- 멀티쓰레드 프로세스 = 자원 + thread + thread + . . .
2. 멀티쓰레딩
- 하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것
- 아주 짧은 시간 동안 여러 작업을 번갈아 가며 수행함으로써 동시에 여러 작업이 수행되는 것처럼 보이게 하는 것
- CPU의 사용률을 향상시킨다.
- 자원을 보다 효율적으로 사용할 수 있다.
- 사용자에 대한 응답성이 향상된다.
- 작업이 분리되어 코드가 간결해진다.
- 동기화(synchronization), 교착상태(deadlock) 같은 문제들을 고려해야 한다.
- 각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야 한다.
3. 쓰레드의 구현과 실행
- 구현하는 두가지 방법은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다.
- 다른 클래스를 상속받을때에는 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
class Thread1 extends Thread { public void run() { for ( int i = 0 ; i < 5 ; i++) { System.out.println(getName()); // 조상 Thread의 getName()을 호출 } // for } // run } // Thread1 class Thread1_1 implements Runnable { @Override public void run() { for ( int i = 0 ; i < 5 ; i++) { // Thread.currentThread(); // 현재 실행 중인 Thread를 반환한다. System.out.println(Thread.currentThread().getName()); } // for } // run } // Thread1_1 public class ThreadTest { public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread1_1 t = new Thread1_1(); Thread t2 = new Thread(t); // 생성자 Thread(Runnable target) t1.start(); t2.start(); } // main } // ThreadTest /* * 결과 * * Thread-0 * Thread-0 * Thread-0 * Thread-0 * Thread-0 * Thread-1 * Thread-1 * Thread-1 * Thread-1 * Thread-1 */ |
4. start(), run()
- run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 속한 메서드 하나를 호출하는 것이다.
- start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 다음에 run()을 호출해서 저장되게 한다.
- 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
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
|
class Human2 extends Thread { String name; public Human2(String name) { this .name = name; } // Human2 생성자 public void run() { for ( int i = 1 ; i <= 10 ; i++) { System.out.println(name + " 이(가) " + "음식" + " 을(를) 먹어요 " + i); try { Thread.sleep( 500 ); } catch (Exception e) { e.printStackTrace(); } // try - catch } // for } // run } // Human public class ThreadTest2 { public static void main(String[] args) { Human2 human1 = new Human2( "갱짱" ); Human2 human2 = new Human2( "이갱짱" ); //human1.run(); //human2.run(); human1.start(); human2.start(); } // main } // ThreadTest |
5. 싱글쓰레드와 멀티쓰레드
- 싱글쓰레드 : 두 개의 작업을 하나의 쓰레드로 처리하는 경우
- 멀티쓰레드 : 두 개의 작업을 두개의 쓰레드로 처리하는 경우
- 단순히 CPU만을 사용하는 계산작업이라면 작업전환(context switching)에 시간이 안걸리는 싱글쓰레드가 효율적이다.
- 하지만 CPU이외의 자원을 사용하는 작업의 경우에는 멀티쓰레드 프로세스가 더 효율적이다.
6. 쓰레드의 우선순위(Proiority)
- 우선순위라는 속성(멤버변수)을 가지고 있는데, 이 우선순위의 값에 따라 쓰레드가 얻는 실행시간이 달라진다.
- 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 서로 다르게 지정하여 특정 쓰레드가 더 많은 작업시간을 갖도록 할 수 있다.
- 쓰레드가 가질 수 있는 우선순위의 범위는 1 ~ 10 이며, 숫자가 높을수록 우선순위가 높다.
- 쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속받는다.
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
|
// void setPriority(int newPriority) : 쓰레드의 우선 순위를 지정한 값으로 변경한다. // int getPriority() : 쓰레드의 우선순위를 반환한다. // public static final int MAX_PRIORITY = 10 : 최대우선순위 // public static final int MAX_PRIORITY = 1 : 최소우선순위 // public static final int MAX_PRIORITY = 5 : 보통우선순위 class Thread_1 extends Thread { public void run() { for ( int i = 0 ; i < 300 ; i++) { System.out.print( "-" ); for ( int x = 0 ; x < 10000000 ; x++); // 작업을 지연시키기 위한 for 문 } // for } // run } // Thread_1 class Thread_2 extends Thread { public void run() { for ( int i = 0 ; i < 300 ; i++) { System.err.print( "|" ); for ( int x = 0 ; x < 10000000 ; x++); // 작업을 지연시키기 위한 for 문 } // for } // run } // Thread_2 public class ThreadPriorityTest { public static void main(String[] args) { Thread_1 th1 = new Thread_1(); Thread_2 th2 = new Thread_2(); th2.setPriority( 8 ); System.out.println( "Priority of th1 (-) : " + th1.getPriority()); System.out.println( "Priority of th2 (|) : " + th2.getPriority()); th1.start(); th2.start(); } // main } // ThreadPriorityTest /* * 결과 * * Priority of th1 (-) : 5 * Priority of th2 (|) : 7 * -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ |
6. 쓰레드 그룹(thread group)
- 서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것.
- 쓰레드 그룹에 다른 쓰레드 그룹을 포함 시킬 수 있다. 보안상의 이유로 도입된 개념이다.
- 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드를 변경할 수는 없다.
- 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 한다.
- 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 'main쓰레드 그룹'에 속한다.
- 쓰레드를 쓰레드 그룹에 포함시키려면 Thread의 생성자를 이용해야한다.
7. 데몬 쓰레드(daemon thread)
- 일반 쓰레드(non-daemon thread)의 작업을 돕는 보조적인 역할을 수행한다.
- 일반 쓰레드가 모두 종료되면 자동적으로 종료된다.
- 가비지컬렉터, 워드프로세서의 자동저장, 화면자동갱신 등에 사용된다.
- 무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.
- boolean is Daemon() : 쓰레드가 데몬 쓰레드인지 확인한다. 데몬 쓰레드이면 true를 반환한다.
- void setDaemon(boolean on) : 쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경한다. 매개변수 on의 값을 true로 지정하면 데몬 쓰레드가 된다.
- setDaemon은 반드시 start()를 호출하기 전에 실행되어야 한다.
8. 쓰레드의 실행제어
- 쓰레드의 생명 주기
- 쓰레드의 스케줄링과 관련된 메서드
반환형 | 메서드 | 설명 |
void | interrupt() | sleep()이나 join()에 의해 일시정지상태인 쓰레드를 실행 대기 상태로 만든다. 해당 쓰레드에서는 InterruptedException이 발생함으로써 일시정지상태를 벗어나게 된다. |
void void void |
join() join(long millis) join(long millis, int nanos) |
지정된 시간동안 쓰레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. |
void | resume() | suspend()에 의해 일시정지상태에 있는 쓰레드를 실행대기상태로 만든다. |
static void static void |
sleep(long millis) sleep(long millis, int nanos) |
지정된 시간(천분의 일초 단위)동안 쓰레드를 일시정지시킨다. 지정한 시간이 지나고 나면, 자동적으로 다시 실행대기상태가 된다. |
void | stop() | 쓰레드를 즉시 종료시킨다. 교착상태(dead-lock)에 빠지기 쉽기 때문에 deprecated 되었다. |
void | suspend() | 쓰레드를 일시 정지시킨다. resume()을 호출하면 |
static void | void yield() | 실행 중에 다른 쓰레드에게 양보(yield)하고 실행대기상태가 된다. |
- 쓰레드의 상태
상태 | 설명 |
NEW | 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태 |
RUNNABLE | 실행 중 또는 실행 가능한 상태 |
BLOCKED | 동기화블럭에 의해서 일시정지된 상태(LOCK이 풀릴 때 까지 기다리는 상태) |
WAITING, TIMED_WAITING | 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnable) 일시 정지상태, TIMED_WAITING은 일시정지시간이 지정된 경우를 의미 |
TERMINATED | 쓰레드의 작업이 종료된 상태 |
- 쓰레드를 생성하고 start()를 호출하면 바로 실행되는 것이 아니라 실행대기열(큐와같은 구조)에 저장되어 자신의 차례가 될 때까지 기다려야 한다.
- 실행대기상태에 있다가 자신의 차례가 되면 실행상태가 된다.
- 주어진 실행시간이 다되거나 yield()를 만나면 다시 실행대기상태가 되고 다음 차례의 쓰레드가 실행상태가 된다.
- 실행 중에 suspend(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 될 수 있다. I/O block은 입출력작업에서 발생하는 지연상태를 말한다.
- 지정된 일시정지시간이 다되거나(time-out), notify(), resume(), interrupt()가 호출되면 일시정지 상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다리게 된다.
- 실행을 모두 마치거나 stop()이 호출되면 쓰레드는 소멸된다.
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
|
// 한 쓰레드의 작업의 중간에 다른 쓰레드의 작업이 필요할 때 join()을 사용한다. class ThreadJoin_1 extends Thread { public void run() { for ( int i = 0 ; i < 300 ; i++) { System.out.print( "-" ); } // for } // run } // ThreadJoin_1 class ThreadJoin_2 extends Thread { public void run() { for ( int i = 0 ; i < 300 ; i++) { System.out.print( "+" ); } // for } // run } // ThreadJoin_1 public class ThreadJoinTest { public static void main(String[] args) { ThreadJoin_1 th1 = new ThreadJoin_1(); ThreadJoin_2 th2 = new ThreadJoin_2(); th1.start(); try { th1.join(); } catch (InterruptedException e) { e.printStackTrace(); } // try - catch System.out.println(); th2.start(); } // main } // ThreadJoinTest /* * 결과 * * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ |
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
// sleep()는 쓰레드를 지정된 시간동안 일시정지 상태가 되도록 한다. 천분의 일초 단위이다. // suspend()가 호출되면 쓰레드는 일시정지 상태가 되고 // resume()이 호출되면 다시 실행 대기 상태가 된다. // stop()을 호출하면 쓰레드는 즉시 종료된다. // interrupt()는 InterruptedException을 발생시켜서, sleep(), join(), wait()에 의해 일시정지상태인 쓰레드를 실행 대기 상태로 만든다. // 그러나, 호출되었을 때 sleep(), join(), wait()에 의한 일시정지상태가 아니라면 아무 일도 일어나지 않는다. class MyTherdTest implements Runnable { boolean suspended = false ; boolean stopped = false ; Thread th; MyTherdTest(String name) { th = new Thread( this , name); // Thread(Runnable r, String name) } // MyMyTherdTest 생성자 @Override public void run() { String name = Thread.currentThread().getName(); while (!stopped) { if (!suspended) { System.out.println(name); try { Thread.sleep( 1000 ); } catch (Exception e) { System.out.println(name + " - interrupted" ); } // try - catch } else { Thread.yield(); } // if } // while System.out.println(name + " - stopped" ); } // run public void suspend() { suspended = true ; th.interrupt(); System.out.println( "interrupt() in suspend()" ); } // suspend public void resume() { suspended = false ; } // resume public void stop() { stopped = true ; th.interrupt(); System.out.println( "interrupt() in stop()" ); } // stop public void start() { th.start(); } // start } // MyTheradTest public class TheradTest { public static void main(String[] args) { MyTherdTest th1 = new MyTherdTest( "●" ); MyTherdTest th2 = new MyTherdTest( "★★" ); MyTherdTest th3 = new MyTherdTest( "◆◆◆" ); th1.start(); th2.start(); th3.start(); try { Thread.sleep( 2000 ); th1.suspend(); Thread.sleep( 2000 ); th2.suspend(); Thread.sleep( 3000 ); th1.resume(); Thread.sleep( 3000 ); th1.stop(); th2.stop(); Thread.sleep( 2000 ); th3.stop(); } catch (Exception e) { e.printStackTrace(); } // try - catch } // main } // TheradTest /* * 실행 결과 ● ◆◆◆ ★★ ● ◆◆◆ ★★ ● interrupt() in suspend() ◆◆◆ ★★ ● - interrupted ★★ ◆◆◆ ◆◆◆ interrupt() in suspend() ★★ ★★ - interrupted ◆◆◆ ◆◆◆ ◆◆◆ ● ◆◆◆ ● ◆◆◆ ● ◆◆◆ interrupt() in stop() interrupt() in stop() ★★ - stopped ● - stopped ◆◆◆ ◆◆◆ interrupt() in stop() ◆◆◆ - interrupted ◆◆◆ - stopped */ |
9. 쓰레드의 동기화
- 멀티쓰레드 프로세스의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업을 하기 때문에 동기화가 필요하다.
- 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 락(lock)을 걸어서 데이터의 일관성을 유지한다.
- synchronized 키워드를 통해 작업과 관련된 공유데이터에 lock를 건다.
- 특정 객체에 lock을 걸 때 : synchronized(객체의 참조변수) { /* ... */ }
- 메서드에 lock을 걸 때 : public synchronized void calcSum() { /* ... */ }
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
48
|
class ATM implements Runnable { private long depositeMoney = 10000 ; @Override public void run() { for ( int i = 0 ; i < 10 ; i++) { try { Thread.sleep( 1000 ); } catch (Exception e) { e.printStackTrace(); } // try - catch if (getDepositeMoney() <= 0 ) break ; synchronized ( this ) { withDraw( 1000 ); } // synchronized } // for } // run public void withDraw( long howMuch) { if (getDepositeMoney() > 0 ) { depositeMoney -= howMuch; System.out.print(Thread.currentThread().getName() + " , " ); System.out.printf( "잔액 : %,d 원 %n" , getDepositeMoney()); } else { System.out.print(Thread.currentThread().getName() + " , " ); System.out.println( "잔액이 부족합니다" ); } // if } // withDraw public long getDepositeMoney() { return depositeMoney; } // getDepositeMoney } // ATM public class SynchronizedTest { public static void main(String[] args) { ATM atm = new ATM(); Thread mother = new Thread(atm, "mother" ); Thread son = new Thread(atm, "son" ); mother.start(); son.start(); } // main } // SynchronizedTest |
10. wait(), notify()
- 동기화의 효율을 높이기 위해 사용한다.
- Object 클래스의 정의되어 있으므로, 모든 객체에서 호출이 가능하다.
- 동기화 블록내에서만 사용할 수 있다.
- wait() : 객체의 lock을 풀고 해당 객체의 쓰레드를 대기상태로 둔다.
- notify() : 대기중인 쓰레드 중의 하나를 깨운다.
- notifyAll() : 대기중인 모든 쓰레드를 깨운다. 호출된 객체의 대기 중인 쓰레드만 해당 된다.
=================================
=================================
=================================
'프로그래밍 관련 > 언어들의 코딩들 C++ JAVA C# 등..' 카테고리의 다른 글
자바, 안드로이드 java android URL주소 영문이외의 한글이나 일본어 문자열이 들어간 주소가 안될때 'UTF-8'로 주소를 인코딩하여 읽을수 있게 해결하기 관련 (0) | 2015.11.18 |
---|---|
자바, 안드로이드 문자열을 바이트 길이구하기, 또는 길이로 제한, 인코딩에 따른 바이트 복사 (0) | 2015.09.02 |
[JAVA] 실행파일 만들기. exe 파일 만들기 등등 (0) | 2015.08.17 |
java 프레임, JPanel 패널에 백그라운드에 이미지설정 또는 투명값 설정 관련 (0) | 2015.03.09 |
자바 현재 view나 frame 또는 panel 에 들어간 오브젝트 컴포넌트가 있는지 알아보기 (0) | 2015.03.09 |
댓글 영역