멀티 스레드
프로세스(process)
- 실행중인 하나의 애플리케이션
- 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행하는 것
스레드(thread)
- 프로세스 내부에서의 코드 실행 흐름
멀티 프로세스(multi process)
하나의 애플리케이션은 멀티 프로세스를 만들기도 하는데,
예를 들어 메모장을 2개 열어 작성한다면 2개의 메모장 프로세스 즉, 멀티 프로세스가 생성된 것
멀티 태스킹(multi tasking)
비슷한 의미로, 두 가지 이상의 작업을 동시에 처리하는 걸 멀티 태스킹이라고 하는데
운영체제는 멀티 태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다 적절히 할당해주고, 병렬로 실행시킨다
음악을 들으며 워드로 문서 작성하는 것을 예로 들 수 있다.
하지만, 멀티 태스킹과 멀티 프로세스는 완전히 같진 않은데
애플리케이션 중에는 한 프로세스 내에서 멀티 태스킹을 할 수 있는 것도 있기 때문
메신저를 예로 들 수 있는데 메신저는 채팅 기능을 제공하면서 동시에 파일 전송 기능을 수행하기도 한다
이처럼 하나의 프로세스가 두 가지 이상의 작업을 처리하게 하기 위해서는 멀티 스레드를 사용
스레드
- 한 가지 작업을 실행하기 위해 순차적으로 실행할 코드를 실처럼 이어놓은 것
멀티 프로세스에서 각 프로세스는 서로 독립적이라
한 프로세스에서 오류가 발생해도 다른 프로세스에 영향을 미치지 않는데,
멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 각 스레드는 다른 스레드에 영향을 미친다
멀티 스레드의 다양한 쓰임
- 대용량 데이터의 처리 시간을 줄이기 위해 데이터를 분할해서 병렬로 처리할 때
- UI를 갖고 있는 애플리케이션에서 네트워크 통신을 하기 위해
- 다수 클라이언트의 요청을 처리하는 서버를 개발할 때
메인 스레드
- 자바의 모든 애플리케이션은 메인 스레드가 main() 메소드를 실행하며 시작
- main() 메소드의 첫 코드부터 아래로 순차적 실행
- main() 메소드의 마지막 코드를 실행하거나 return문을 만나면 실행 종료
- 필요에 따라 작업 스레드들을 만들어 병렬로 코드를 실행 가능
- 즉, 멀티 스레드를 생성해서 멀티 태스킹 수행
- 싱글 스레드 애플리케이션
- 메인 스레드 종료 시 프로세스 종료
- 멀티 스레드 애플리케이션
- 실행 중인 스레드가 하나라도 있을 시 종료되지 않음
작업 스레드 생성과 실행
멀티 스레드 애플리케이션을 개발하기 위해서
몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성해야 한다
- 메인 작업 이외의 추가적인 병렬 작업의 수만큼 스레드 생성
- 자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요
- java.lang.Thread 클래스를 직접 객체화하여 생성
- Thread 클래스를 상속해 하위 클래스를 만들어 생성
Thread 클래스로부터 직접 생성
Runnable을 매개값으로 갖는 생성자를 호출
Thread thread = new Thread(Runnable target);
Runnable
- 작업 스레드가 실행할 수 있는 코드를 갖고 있는 객체
- 인터페이스 타입이라 구현 객체를 만들어 대입
- run() 메소드가 정의되어 있음
- 구현 클래스는 run()을 재정의해서 작업 스레드가 실행할 코드를 작성
Runnable은 실제 스레드가 아닌 작업 내용을 갖고 있는 객체이기 때문에
이를 매개값으로 해서 Thread 생성자를 호출해야 작업 스레드가 생성
// Runnable 구현 클래스
class Task implements Runnable {
public void run() {
스레드가 실행할 코드;
}
}
Runnable task = new Task();
Thread thread = new Thread(task);
Thread 생성자를 호출할 때 Runnable 익명 객체를 매개값으로 사용하면 코드 절약 가능
Thread thread = new Thread(new Runnable() {
public void run() {
스레드가 실행할 코드;
}
} );
작업 스레드를 생성했어도 즉시 실행되지 않고 start() 메소드를 호출해야 실행
start() 메소드가 호출되면 매개값으로 받은 Runnable의 run()메소드를 실행하며 작업 처리
thread.start();
메인 스레드는 동시에 두 가지 작업을 처리할 수 없기 때문에
동시에 출력하기 위해서는 두 작업 중 하나를 메인 스레드가 아닌 다른 스레드에서 실행시켜야 한다
* 0.5초 주기로 비프음을 발생시키면서 동시에 출력하는 작업
- 메인 스레드만 이용한 경우
- 비프음을 발생한 다음 출력을 시작(동시 X)
public class BeepPrintExample1{
public static void main(String[] args){
Toolkit toolkit = Toolkit.getDefaultToolkit(); // Toolkit 객체 얻기
for (int i = 0; i < 5; i++) {
toolkit.beep(); // 비프음 발생
try {Thread.sleep(500);} catch (Exception e) {} // 0.5초간 일시 정지
}
for (int i = 0; i < 5; i++) {
System.out.println("띵");
try {Thread.sleep(500);} catch (Exception e) {} // 0.5초간 일시 정지
}
}
}
- 메인 스레드 + 작업 스레드
- 출력은 메인 스레드가, 비프음을 들려주는 것은 작업 스레드가 담당
- 작업을 정의하는 Runnable 구현 클래스를 작성 후 메인 스레드에 적용
- 동시 실행
- Runnable을 익명 구현 객체로 대체할 수도 있다
// 비프음을 들려주는 작업 정의
public class BeepTask implements Runnable {
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i=0; i<5; i++) {
toolkit.beep();
try { Thread.sleep(500); } catch (Exception e) {}
}
}
}
// 메인 스레드와 작업 스레드 동시 실행
public class BeepPrintExample2 {
public static void main(String[] args) {
Runnable beepTask = new BeepTask(); // BeepTask 객체 생성
Thread thread = new Thread(beepTask); // 작업 스레드 생성
thread.start(); // BeepTask 실행 및 for문 실행하여 동시 출력
for(int i=0; i<5; i++) {
System.out.println("띵");
try { Thread.sleep(500); }
catch (Exception e) {}
}
}
}
Thread 하위 클래스로부터 생성
작업 스레드가 실행할 작업을 Runnable로 만들지 않고 Thread의 하위 클래스로 작업 스레드를 정의
Thread 클래스를 상속한 후 run() 메소드 재정의
public class WorkerThread extends Thread {
@Override
public void run() {
스레드가 실행할 코드;
}
}
Thread thread = new WorkerThread();
Thread 익명 객체로 작업 스레드 객체를 생성하면 코드 절약 가능
Thread thread = new Thread() {
public void run() {
스레드가 실행할 코드;
}
};
Runnable과 마찬가지로 작업 스레드 객체에서 start() 메소드를 호출,
작업 스레드는 자신의 run() 메소드를 실행
thread.start();
* 0.5초 주기로 비프음을 발생시키면서 동시에 출력하는 작업
- 비프음을 들려주는 스레드
public class BeepThread extends Thread {
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for (int i = 0; i < 5; i++) {
toolkit.beep();
try { Thread.sleep(500);} catch (Exception e) {}
}
}
}
- 메인 스레드와 작업 스레드가 동시에 실행
public class BeepPrintExample4 {
public static void main(String[] args) {
Thread thread = new BeepThread(); // BeepThread 객체 생성
thread.start(); // BeepThread의 run() 메소드 실행 및 for문 실행하여 동시 출력
for (int i = 0; i < 5 ; i++) {
System.out.println("띵");
try { Thread.sleep(500);} catch(Exception e) {}
}
}
}
스레드의 이름
스레드는 자신의 이름을 갖고 있다
- 디버깅할 때 어떤 스레드가 어떤 작업을 하는지 조사할 목적으로 가끔 사용
메인 스레드
- 'main' 이라는 이름을 갖음
직접 생성한 스레드
- 자동적으로 'Thread-n' 이라는 이름으로 설정
- n은 스레드 번호
- Thread-n 대신 다른 이름으로 설정을 희망 시
- Thread 클래스의 setName() 메소드로 변경
thread.setName("스레드 이름");
- 스레드의 이름을 알고 싶은 경우
- Thread 클래스의 getName() 메소드 호출
thread.getName();
스레드 객체의 참조
- 인스턴스 메소드
- 스레드 객체의 참조가 필요
- setName(), getName()은 Thread 클래스의 인스턴스 메소드에 해당
- 스레드 객체의 참조를 갖고 있지 않은 경우
- Thread 클래스의 정적 메소드인 currentThread() 이용
- 현재 스레드의 참조를 얻을 수 있음
Thread thread = Thread.currnetThread();