[ 멀티 스레드 (Multi Thread) ]
[1] 멀티 스레드의 개념
1. 프로세스와 스레드
- 프로세스 (process)
: 실행 중인 하나의 프로그램을 의미
하나의 프로그램이 다중 프로세스 만들기도 한다.
ex) Chrome 브라우저를 두개 실행 -> 두개의 Chrome 프로세스 생성
- 멀티 태스킹 (multi tasking)
: 두 가지 이상의 작업을 동시에 처리하는 것
멀티 프로세스나 멀티 스레드나 둘다 멀티 태스킹에 해당된다.
- 멀티 프로세스 (multi process)
: 독립적으로 프로그램들을 실행하고 여러가지의 작업을 처리하는 것
- 멀티 스레드 (multi thread)
: 한 개의 프로그램을 실행하고 내부적으로 여러가지의 작업을 처리하는 것
2. 메인 스레드 (main thread)
- 모든 자바 프로그램은 메인 스레드가 main( ) 메서드를 실행하면서 시작된다.
- 프로그램은 main( ) 메서드의 첫 코드부터 아래 방향으로 순차적으로 실행된다.
- 실행 종료 조건
: 마지막 코드 실행하거나 return 문 만날때 프로그램이 종료된다.
- 메인 스레드 (main thread) 는 작업 스레드들을 만들어서 병렬로 코드들을 실행한다.
: 멀티 스레드를 생성하여 멀티 태스킹을 수행한다.
- 프로세스 종료
(1) 싱글 스레드 : 메인 스레드가 종료하면 프로세스도 종료한다.
(2) 멀티 스레드 : 실행 중인 스레드가 하나라도 있으면, 프로세스는 종료 안한다.
import java.awt.Toolkit;
public class BeepPrintExample1 {
// 하나의 Main Thread가 하나의 main() 메서드를 실행한다.
public static void main(String[] args) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i=0; i<5; i++) {
toolkit.beep(); // 소리가 beep 소리가 나게 만드는 구문
try { Thread.sleep(500); } catch(Exception e) {}
} // 소리 출력
for(int i=0; i<5; i++) {
System.out.println("띵");
try { Thread.sleep(500); } catch(Exception e) {}
} // 소리가 끝나면 띵 출력되게 한다.
}
}
[2] 작업 스레드 생성과 실행
- 멀티 스레드로 실행하는 애플리케이션을 개발하려면 먼저 몇개의 작업들을 병렬로
실행할지 말지 결정하고, 각 작업별로 스레드를 생성해야 한다.
- 작업 스레드는 객체로 생성하는 방법
(1) java.lang.Thread 클래스를 직접 객체화해서 작업 스레드 생성
(2) Thread 클래스를 상속받는 하위 클래스를 만들어 작업 스레드 생성
1. Thread 클래스로부터 작업 스레드 직접 생성
: Runnable 타입의 변수를 매개값으로 갖는 생성자를 호출해야 한다.
- 방법 1 : Runnable 구현 객체 만들어서 작업 스레드 생성
* 단점(?) : 작업 스레드 하나 만들려고 구현클래스를 만들어서 한번만 사용하고 더이상 사용하지 않는다.
- - > 유지보수가 좋지 않다 :(
- 방법 2 : 익명 구현 객체 만들어서 작업 스레드 생성
- 방법 3 : 람다식 만들어서 작업 스레드 생성
(* 람다식 : 익명구현객체를 간략하게 표현한 식*)
import java.awt.Toolkit;
// Runnable 인터페이스 구현한 BeepTask 구현 클래스
public class BeepTask implements Runnable {
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i=0; i<5; i++) {
toolkit.beep();
try {
Thread.sleep(500); // Thread.sleep(500)은 현재 스레드를 500ms 정지한다.
} catch(Exception e) {
}
}
}
}
import java.awt.Toolkit;
public class BeepPrintExample2 {
// Main Thread가 main() 메서드를 실행
public static void main(String[] args) {
/** 띵이라는 메세지와 띵 소리가 동시에 출력되게 만들자. **/
// 1. Thread 클래스로부터 직접 Thread 객체를 생성
// how1 : Runnable 구현 객체
// 이 방법의 문제점(?) : BeepTask()를 만들고 한번밖에 사용 안함 --> 유지보수가 좋지 않다.
/*Runnabl beepTask = new BeepTask();
// 작업 스레드 생성 (Multi-Thread 환경)
Thread thread = new Thread(beepTask); // Main 스레드에서 작업 스레드 생성
*/
// how2 : 익명 구현 객체 <-- how1 방법의 문제점 보완
/*Thread thread = new Thread(new Runnable() {
// 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) {}
}
}
});*/
// how3 : 람다식 (익명구현객체를 간략하게 표현한 식)
Thread thread = new Thread(() -> {
// () 가 의미하는 것이 how2방법에서 같은것은
// new 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) {}
}
});
thread.start(); // <- 작업 스레드가 실행함 (작업 스레드에서 실행)
for(int i=0; i<5; i++) { // <-- 메인 스레드가 실행함 (메인 스레드에서 실행)
System.out.println("띵");
try { Thread.sleep(500); } catch(Exception e) {}
}
}
}
2. Thread 하위 클래스로부터 작업 스레드 생성
: Thread 클래스 상속받는 하위 클래스를 만든 후 run 메소드를 재정의해서 스레드가 실행할 코드 작성한다.
import java.awt.Toolkit;
// Thread의 하위 클래스 (Thread 클래스를 상속받는 클래스)
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) {}
}
}
}
import java.awt.Toolkit;
public class BeepPrintExample3 { // 멀티 태스킹
public static void main(String[] args) {
// 2. Thread 하위 클래스로부터 생성
// how1 : Thread 클래스의 자식 클래스를 만들어서 자식 객체를 생성하여 이용
// 이 방법의 단점 : 객체 하나 만들기 위해 클래스를 만들어줘야함 --> 유지보수 좋지않음
// Thread thread = new BeepThread(); // 자동형변환: 부모형 <- 자식형
// how2 : 익명 자식 객체 이용 <-- how1 방법의 단점 보완..?
Thread thread = new 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) {
}
}
}
};
thread.start();
for(int i=0; i<5; i++) {
System.out.println("띵");
try { Thread.sleep(500); } catch(Exception e) {}
}
}
}
- 스레드의 이름
- 메인 스레드의 이름 : main
- 작업 스레드의 이름을 따로 지정 안해준 작업 스레드 이름(자동으로 설정됨) 얻는 방법 : thread.getName();
- 작업 스레드의 이름을 변경하는 방법 : thread.setName("스레드 이름");
- 코드 실행하는 현재 스레드 객체의 참조 얻기 : Thread thread = Thread.currentThread(); (,,, 이건 뭔지 잘 모르겠다,,)
public class ThreadA extends Thread {
public ThreadA() {
setName("ThreadA"); // 작업 스레드 이름 변경
}
public void run() {
for(int i=0; i<2; i++) {
System.out.println(getName() + "가 출력한 내용");
}
}
}
public class ThreadB extends Thread {
public void run() {
for(int i=0; i<2; i++) {
System.out.println(getName() + "가 출력한 내용");
}
}
}
public class ThreadNameExample {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
System.out.println("프로그램 시작 스레드 이름: " + mainThread.getName());
// Thread 클래스를 상속받은 ThreadA 하위 클래스
// 이름 지정된 메서드 구현되어 있음 --> 내가 원하는 이름으로 출력됨
ThreadA threadA = new ThreadA();
System.out.println("작업 스레드 이름: " + threadA.getName());
threadA.start();
// Thread 클래스를 상속받은 ThreadB 하위 클래스
// 이름 지정하는 메서드 구현 X --> 자동으로 지정된 이름으로 출력됨
ThreadB threadB = new ThreadB();
System.out.println("작업 스레드 이름: " + threadB.getName());
threadB.start();
}
}
[3] 스레드 우선순위
- 동시성 : 멀티 작업을 위해 하나의 코어(하나의 cpu)에서 멀티 스레드가 번갈아 가며 실행하는 성질
--> 하나의 코어에서 조금씩 여러개의 스레드를 번갈아가며 실행하는데 각 스레드마다
우선순위를 부여해서 실행할 수 있다.
- 병렬성 : 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질
--> 병렬성이 동시성보다 속도가 더 빠르다.
- 스레드의 개수가 코어의 수보다 많을 경우
- 스레드를 어떤 순서로 동시성으로 실행할건지 결정하는 것 : 스레드 스케쥴링(Thread Scheduling)
- 스케쥴링에 의해 스레드들은 번갈아 가면서 run( ) 메서드를 조금씩 실행하는 것 : time - sharing
1) 우선 순위 (Priority) 방식 사용
- 우선 순위 방식 : 우선 순위가 높은 스레드가 더 많이 실행하도록 스케쥴링 해주는 것
* 1 (우선순위 낮음) ~ 10 (우선순위 높음) 까지의 우선순위 값을 가질 수 있고 기본 디폴트값은 5이다.
- 우선 순위 방식은 코드로 제어 가능하다.
2) 순환 할당 (Round-Robin) 방식 사용
- 순환 할당 방식 : 시간 할당량 (Time Slice)을 정해서 하나의 스레드를 정해진 시간만큼 실행해주는 것
- 순환 할당 방식은 코드로 제어할 수 없다.
public class CalcThread extends Thread {
public CalcThread(String name) {
setName(name);
}
public void run() {
for(int i=0; i<2000000000; i++) {
}
System.out.println(getName());
}
}
public class PriorityExample {
public static void main(String[] args) {
for(int i=1; i<=10; i++) {
Thread thread = new CalcThread("thread" + i);
if(i != 10) {
thread.setPriority(Thread.MIN_PRIORITY);
// MIN_PRIORITY = 우선순위 1
} else {
thread.setPriority(Thread.MAX_PRIORITY);
// MAX_PRIORITY = 우선순위 10
}
thread.start();
}
}
}
'Studying' 카테고리의 다른 글
동기화 메서드 (0) | 2024.01.30 |
---|---|
리다이렉트 (Redirect) (0) | 2024.01.02 |
Rest vs Restful (2) | 2024.01.02 |
HTTP 상태 코드 (2) | 2023.10.30 |
클라우드 서비스 개념 공부 (2) | 2023.07.17 |