[Java] Thread
프로세스 Process
실행 중인 프로그램(Program).
프로그램을 수행하는 데 필요한 데이터와 메모리 등의 자원, 쓰레드로 구성되어 있다.
최소 하나 이상의 쓰레드가 존재한다.
싱글쓰레드 프로세스 (Single-Threaded Process)
하나의 쓰레드를 가진 프로세스.
두 개 이상의 작업을 처리할 때 한 작업을 마친 후에 다른 작업을 시작한다.
싱글 코어는 단순히 CPU만을 사용하는 계산 작업에 효율적이다.
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 500; i++) {
System.out.printf("-");
}
System.out.printf("Time : " + (System.currentTimeMillis() - start));
for (int i = 0; i < 500; i++) {
System.out.printf("|");
}
System.out.printf("Time2 : " + (System.currentTimeMillis() - start));
}
}Plain Text
1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Time : 67||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||Time2 : 78멀티쓰레드 프로세스 (Multi-Threaded Process)
둘 이상의 쓰레드를 가진 프로세스.
두 개 이상의 작업을 처리할 때 번갈아 가면서 작업을 수행한다.
이 때 쓰레드간의 작업 전환에 시간이 걸리기 때문에 싱글쓰레드 보다 작업 시간이 더 걸릴 수 있다.
쓰레드들이 서로 다른 자원을 사용해서 작업할 때 효율적 → 한 쓰레드가 작업 중이면 다른 쓰레드는 대기한다.
싱글 코어
병행(Concurrent). 작업이 절대 겹치지 않는다.
멀티 코어
병렬(Parallel). 쓰레드가 동시에 수행될 수 있어 작업이 겹치는 부분이 발생한다.
화면(Console)이라는 자원을 놓고 두 쓰레드가 경쟁하게 된다.
Java
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
public class Test { // (Multi-Core, Multi-Thread)
static long startTime = 0;
public static void main(String[] args) {
ThreadEx t = new ThreadEx();
t.start();
long start = System.currentTimeMillis();
for (int i = 0; i < 500; i++) {
System.out.printf("-");
}
System.out.printf("tiem2 : " + (System.currentTimeMillis() - start));
}
}
class ThreadEx extends Thread {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.printf("|");
}
System.out.printf("tiem : " + (System.currentTimeMillis() - Test.startTime));
}
}Plain Text
1
----------------------------------------------------------------------------------------------------------------------||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||tiem2 : 93tiem : 1518921785720불확실성
OS의 프로세스 스케줄러의 영향을 받아 매 순간 상황에 따라 프로세스에게 할당되는 실행시간이 일정하지 않다.
용어
- 작업 전환(Context Switching) : 현재 진행 중인 작업의 상태(다음에 실행해야할 위치(PC, 프로그램 카운터) 등의 정보)를 저장하고 읽는 것.
- 대기 시간 : 한 쓰레드가 화면에 출력하고 있는 동안 다른 쓰레드는 출력이 끝나기를 기다린다.
쓰레드 Thread
프로세스의 자원을 이용해서 실제로 작업을 수행하는 것.
독립적인 작업을 수행하기 위해 개별적인 메모리 공간(호출스택)을 필요로 한다.
→ 프로세스의 메모리 한계에 따라 생성할 수 있는 쓰레드의 수가 결정된다.
실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
불확실성
JVM의 쓰레드 스케줄러에 의해 어떤 쓰레드가 얼마동안 실행될 것인지 결정되기 때문에 쓰레드에게 할당되는 시간이 일정하지 않다.
JVM의 종류에 따라 쓰레드 스케줄러의 구현 방법이 다를 수 있다.
멀티쓰레딩 Multi-Threading
하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것.
장점
CPU의 사용률을 향상시킨다.
자원을 보다 효율적으로 사용할 수 있다.
사용자에 대한 응답성이 향상된다.
작업이 분리되어 코드가 간결해진다.
단점
동기화(Synchronization) : 자원을 공유하면서 작업하기 때문에 서로의 작업에 영향을 줄 수 있다.
교착 상태(Deadlock) : 두 쓰레드가 자원을 점유한 상태에서 서로 상대편이 점유한 자원을 사용하려고 기다리느라 진행이 멈춰있는 상태.
구현 1. Thread 클래스를 상속 받는다.
다른 클래스를 상속받을 수 없게 되기 때문에 잘 쓰이지 않는다.
Java
1
2
3
4
5
6
public class Test extends Thread {
@Override
public void run() {
super.run();
}
}Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.logging.Logger;
public class Test extends Thread {
Logger logger = Logger.getLogger(Test.class.getName());
@Override
public void run() {
for (int i = 0; i < 10; i++) {
logger.info("Thread Name : " + getName()); // 쓰레드의 이름을 반환
} // 쓰레드의 이름을 지정하지 않으면 'Thread-번호'의 형식으로 이름이 정해진다.
}
public static void main(String[] args) {
Test test = new Test();
test.start();
}
}Plain Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2월 18, 2018 11:52:22 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0
2월 18, 2018 11:52:23 오전 blog.Test run
정보: Thread Name : Thread-0구현 2. Runnable 인터페이스를 구현한다.
run()만 정의되어 있는 간단한 인터페이스로, 일반적으로 쓰이는 방법이다.
재사용성(Reusability)이 높고, 코드의 일관성(Consistency)을 유지할 수 있다.
Java
1
2
3
public interface Runnable {
public abstract void run();
}쓰레드를 사용할 때, 구현한 클래스의 인스턴스를 생성한 후, 그 인스턴스를 Thread 클래스 생성자의 매개변수로 제공해야 한다.
Thread 클래스의 메서드를 사용할 경우, Thread 클래스의 static 메서드인 currentThread()를 호출하여 쓰레드에 대한 참조를 얻어와야 한다.
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.logging.Logger;
public class Test implements Runnable {
Logger logger = Logger.getLogger(Test.class.getName());
public static void main(String[] args) {
Runnable r = new Test();
Thread t = new Thread(r);
t.start();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
logger.info("Thread Name : " + Thread.currentThread().getName());
}
}
}Plain Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
2월 18, 2018 11:57:10 오전 blog.Test run
정보: Thread Name : Thread-1
start()
쓰레드를 실행한다.
실행 대기중인 쓰레드가 하나라도 있으면 실행 대기 상태에 있다가 자신의 차례가 되어야 실행된다.
새로운 쓰레드가 작업을 실행하는 데 필요한 호출 스택을 생성한 후, run()을 호출하여 생성된 호출 스택에 첫번째로 올라가게 한다.
한번 실행이 종료된 쓰레드는 다시 실행할 수 없다.
Java
1
2
3
4
5
6
Thread t = new Thread(new Test());
t.start();
// t.start(); error
t = new Thread(new Test());
t.start();run()
생성된 쓰레드를 실행시키는 것이라 착각할 수 있는데, 클래스에 선언된 메서드를 호출하는 것일 뿐이다.
Main Thread
main 메서드의 작업을 수행하는 것.
프로그램을 실행하면 기본적으로 하나의 쓰레드를 생성하고, 그 쓰레드가 main 메서드를 호출해서 작업이 수행되도록 한다.
데몬 쓰레드 Daemon Thread
일반 쓰레드(데몬 쓰레드가 아닌 쓰레드)의 작업을 돕는 보조적인 역할을 수행하는 쓰레드.
무한 루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.
일반 쓰레드가 모두 종료되면 강제적으로 자동 종료된다.
데몬 쓰레드가 생성한 쓰레드는 자동적으로 데몬 쓰레드가 된다.
작성과 실행
일반 쓰레드와 동일하게 생성한 후 실행하기 전에 setDaemon(true)를 호출하면 된다.
Java
1
2
3
public final void setDaemon(boolean on) // 쓰레드를 데몬 쓰레드 또는 사용자 쓰레드로 변경
public final boolean isDaemon() // 쓰레드가 데몬 쓰레드인지 확인. 데몬 쓰레드면 true 반환
Java
1
2
3
Thread t = new Thread();
t.setDaemon(true);
t.start();setDaemon 메서드는 바드시 start()를 호출하기 전에 실행되어야 한다.
그렇지 않으면 IllegalThreadStateException이 발생한다.
Example
Java
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
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
public class Test {
public static final Logger logger = Logger.getLogger(Test.class.getName());
public static void main(String[] args) {
ThreadEx1 t1 = new ThreadEx1("Tread1");
ThreadEx2 t2 = new ThreadEx2("Tread2");
t1.start();
t2.start();
}
}
class ThreadEx1 extends Thread {
ThreadEx1(String name) {
super(name);
}
@Override
public void run() {
try {
sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadEx2 extends Thread {
ThreadEx2(String name) {
super(name);
}
@Override
public void run() {
Map map = getAllStackTraces();
Iterator it = map.keySet().iterator();
int x = 0;
while (it.hasNext()) {
Object obj = it.next();
Thread t = (Thread) obj;
StackTraceElement[] ste = (StackTraceElement[]) (map.get(obj));
Test.logger.info("[" + ++x + "] name : " + t.getName() + ", group: " + t.getThreadGroup().getName() + ", daemon: " + t.isDaemon());
for (int i = 0; i < ste.length; i++) {
Test.logger.info(ste[i] + "");
}
Test.logger.info(" ");
}
}
}Plain Text
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
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [1] name : Tread1, group: main, daemon: false
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.Thread.sleep(Native Method)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: blog.ThreadEx1.run(Test.java:28)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보:
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [2] name : Signal Dispatcher, group: system, daemon: true
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보:
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [3] name : Tread2, group: main, daemon: false
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.Thread.dumpThreads(Native Method)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.Thread.getAllStackTraces(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: blog.ThreadEx2.run(Test.java:42)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보:
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [4] name : Finalizer, group: system, daemon: true
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.Object.wait(Native Method)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.ref.ReferenceQueue.remove(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.ref.ReferenceQueue.remove(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보:
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [5] name : Reference Handler, group: system, daemon: true
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.Object.wait(Native Method)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.Object.wait(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.ref.Reference.tryHandlePending(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보:
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [6] name : DestroyJavaVM, group: main, daemon: false
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보:
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: [7] name : Attach Listener, group: system, daemon: true
2월 18, 2018 12:01:13 오후 blog.ThreadEx2 run
정보: getAllStackTraces()를 이용하면 실행 중 또는 대기 상태, 즉 작업이 완료되지 않은 모든 쓰레드의 호출스택을 출력할 수 있다.
JVM은 프로그램 실행시 가비지 컬렉션, 이벤트 처리, 그래픽 처리와 같이
프로그램이 실행되는 데 필요한 보조작업을 수행하는 데몬 쓰레드들을 자동적으로 생성해서 실행시킨다.
이들은 System Thread 또는 Main Thread Group에 속한다.
참고 서적: 자바의 정석 3판 2

[Android] RecyclerView
RecyclerView
Android

[Java] ThreadGroup
Java의 ThreadGroup을 설명한 글입니다.
Java
