프로세스 (process)
- 실행 중인 하나의 프로그램
- 사용자가 애플리케이션을 실행하면 운영체제(O/S)로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는 것.
- 하나의 프로그램이 다중 프로세스를 만들기도 한다.
멀티 태스킹 (multi tasking)
- 두가지 이상의 작업을 동시에 처리하는 것.
멀티 프로세스 - 어플리케이션 단위의 멀티 태스킹
- 독립적으로 프로그램을 실행하고 여러가지 작업을 처리
- 운영체제에서 할당받은 자신의 메모리를 가지고 실행하여, 서로 독립적
- 하나의 프로세스에 오류가 발생해도 다른 프로세스에 영향을 미치지 않는다
멀티 스레드 - 한 개의 어플리케이션 내부에서의 멀티 태스킹
- 스레드(thread) : 한 가지 작업을 실행하기 위해 순차적으로 실행할 코드
- 한 개의 프로그램을 실행하고 내부적으로 여러 가지 작업 처리
- 하나의 프로세스 내에서 여러가지 작업을 처리
- 하나의 스레드에서 오류가 발생하면 다른 스레드도 같이 종료되기 때문에, 예외처리를 잘 해야함.
메인 (main) 스레드
모든 자바 프로그램은 메인 스레드가 main() 메소드를 실행하며 시작되며, main() 메소드의 첫 코드부터 아래로 순차적으로 실행된다.
마지막 코드가 실행되거나, return 문을 만나면 종료된다.
싱글 스레드는 메인 스레드가 종료하면 프로세스도 종료되며,
멀티 스레드는 실행중인 스레드가 하나라도 있다면, 프로세스가 종료되지 않는다.
작업 스레드 생성 방법
- Thread 클래스로부터 직접 생성
Runnable 인터페이스를 매개값으로 갖는 Thread 생성자 호출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class BeepTask implements Runnable {
//인터페이스를 구현하는 구현 클래스
@Override
public void run() {
// 스레드가 실행할 명령문을 기술
for (int i = 0; i < 5; i++)
{
System.out.println("삐~");
try{
Thread.sleep(1000);
}
catch(InterruptedException e )
{
}
}
}
}
|
cs |
<main>
Runnable beepTask = new DeepTask();
Thread thread = new Thread(BeepTask);
thread.start(); // 스레드 실행 메소드
- Thread 하위 클래스로부터 생성
Thread 클래스 상속 후 run 메소드를 재정의해 스레드가 실행할 코드 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class BeepThread extends Thread {
// Thread를 상속받아서 run() 메소드를 재정의해서 사용
@Override
public void run()
{
for (int i = 0;i < 5; i++)
{
System.out.println("삐~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
|
cs |
<main>
Thread thread = new BeepThread();
thread.start();
<익명 함수 이용>
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
|
public class BeepTaskEx2 {
public static void main(String[] args)throws InterruptedException {
// 익명 객체 (인터페이스)를 바로 대입해서 스레드 생성
Thread thread = new Thread(new Runnable() {
@Override
public void run()
{
// 스레드가 실행할 명령문을 기술
for (int i = 0 ; i < 5; i ++) {
System.out.println("삐~");
try {
Thread.sleep(1000);
} catch(InterruptedException e)
{ }
}
}
});
thread.start();
new Thread() // 인스턴스 없이 사용하기
{
@Override
public void run()
{
for (int i= 0; i < 3; i++) {
System.out.println("응애");
try {
Thread.sleep(1000);
}catch (InterruptedException e) {}
}
}
}.run();
}
}
|
cs |
스레드의 이름
메인 스레드의 이름 : main
작업 스레드의 이름 (자동 설정) : Thread-n
스레드 이름 확인 : thread.getName();
스레드 이름 변경 : thread.setName("스레드 이름");
코드 실행하는 현재 스레드 객체의 참조 얻기
Thread thread = Thread.currentThread();
반환 내용 : Thread[호출스레드명, 우선운위, 스레드그룹명]
스레드 우선 순위
동시성 (Concurrentcy)
멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아 가며 실행하는 성질
병렬성 (parallelis)
멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질
스레드 스케줄링
스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서로 동시성으로 실행할 것인가 결정하기 위해 사용
스케줄링에 의해 스레드들은 번갈아 가며 run() 메소드를 조금씩 실행
우선순위 방식 (Priority) - 코드로 제어 가능
우선 순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링
1 ~ 10까지 가질 수 있으며 기본은 5.
하지만 우선순위가 높다고 해서 항상 먼저 실행되는 것은 아님.
우선실행될 확률이 올라가는 것일 뿐...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package schedule;
public class LoopThreadEx {
public static void main(String[] args)
{
for (int i = 1; i <= 10; i++)
{
Thread thread = new LoopThread("thread_" + i);
if (i != 10)
thread.setPriority(Thread.MIN_PRIORITY);
else
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
}
}
|
cs |
순환 할당 (Round-Robin) - 코드로 제어할 수 없음
시간 할당량 (Time-Slice) 정해서 하나의 스레드를 정해진 시간만큼 실행
동기화 메소드와 동기화 블록
동유객체를 사용할 때, 멀티 스레드가 하나의 객체를 공유하면 문제가 생길 수 있다.
스레드가 사용 중인 객체를 다른 스레드가 변경하는 일을 방지하기 위해서 synchronized 키워드를 사용한다.
이 키워드를 사용하면 단 하나의 스레드만 메소드 혹은 블록을 실행할 수 있어, 다른 스레드는 메소드나 블록 실행이 끝날 때 까지 대기해야한다.
동기화 메소드
public synchronized void method() {
}
동기화 블록
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
|
package control;
public class DataBox {
private String data;
public synchronized String getData()
{
if (this.data == null) {
// 데이터 값이 존재하지 않으면
try {
wait(); // 대기상태로 전환
} catch(InterruptedException e) { }
}
String returnValue = data;
System.out.println("ConSumerThread가 읽은 데이터 : " + returnValue);
data = null; // 데이터를 반환한 다음 데이터 초기화
notify(); // 대기상태인 스레드 실행대기로 전환
return returnValue;
}
public synchronized void setData(String data) {
if (this.data != null) {
// 데이터가 존재한다면
try {
wait(); // 대기상태로 전환
} catch (InterruptedException e) {}
}
this.data = data;
System.out.println("ProducerThread가 생성한 데이터 : " + data);
notify(); // 일시 정지 상태인 스레드를 실행대가로 전환
}
// 스레드의 실행 순서는 예측이 불가능하다.
// 만약 처음에 ConsumerThread가 세번 연속 실행이 되었을 경우,
// 데이터가 존재하지 않기 때문에, 첫번째 ConsumerThread는 wait()에 의해 대기상태가 된다.
// 두번째,세번째 ConsumerThread는 synchronized 키워드에 의하여 getData을 실행할 수 없다.
// 이후 ProducerThread에 의해 데이터가 생성되고, notify()에 의해 첫번째 소비자 스레드가 실행된다.
// 이후 흐름 동일.
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package control;
public class ProducerThread extends Thread {
private DataBox dataBox;
public ProducerThread(DataBox dataBox) {
this.dataBox = dataBox;
}
@Override
public void run()
{
for (int i = 1; i <= 3; i++)
{
String data = "Data" + i;
dataBox.setData(data);
}
}
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package control;
public class ConsumerThread extends Thread {
private DataBox dataBox;
public ConsumerThread(DataBox dataBox)
{
this.dataBox = dataBox;
}
@Override
public void run()
{
for (int i = 1; i <= 3; i++)
{
String data = dataBox.getData();
}
}
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package control;
public class ControlThread {
public static void main(String[] args)
{
DataBox dataBox = new DataBox();
ProducerThread prodThread = new ProducerThread(dataBox);
ConsumerThread consThread = new ConsumerThread(dataBox);
prodThread.start();
consThread.start();
}
}
|
cs |
스레드 상태
구분 |
메소드 |
설명 |
일시 정지로 보냄 |
sleep(long millis) |
주어진 시간(밀리세컨드 단위)동안 스레드를 일시 정지 상태도 만듦. 시간이 지나면 자동으로 실행상태로 전환 |
join() |
join() 메소드를 호출한 스레드는 일시 정지 상태. join 메소드를 가진 스레드가 종료되면 실행 대기 상태로 전환 |
wait() |
동기화 블록 내에서 스레드를 일시 정지 상태로 만듦 |
일시정지에서 벗어남 |
interrupt() |
일시 정지 상태일 경우, exception을 발생시켜 ready() 혹은 종료 상태로 전환 |
notify(), notifyAll() |
wait() 메소드로 일시 정지 상태인 스레드를 실행 대기 상태로 만듦 |
실행 대기로 보냄 |
yield() |
실행 상태에서 다른 스레드에게 실행을 양보하고 실행 대기 상태로 전환 |
sleep() : 얼마동안 멈출것인지 밀리 세컨드 단위로 지정.
일시 정지 상태에서 정지가 끝나기 전에 interrupt() 메소드가 호출되면 interruptException 발생. 따라서 예외처리가 필요함.
데몬 스레드 (daemon)
주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드.
주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료됨.
ex) 워드프로세서의 자동저장, 미디어플레이어의 동영상 및 음악 재생, 가비지 컬렉터 등...
스레드를 데몬 스레드로 만들기
주 스레드가 데몬이 될 스레드의 setDaemon(true) 호출
반드시 start() 메소드 호출 전에 setDaemon(true)를 호출해야 함. 그렇지 않으면 IllegalThreadStateException 발생.
isDaemon() 메소드의 리턴값 조사 - true면 데몬 스레드.