이 글은 유튜브 '자바의 정석 - 기초편'을 보고 정리한 글입니다.
📂content
1. 프로세스와 스레드 (process & thread)
- 프로세스
실행 중인 프로그램, 자원(resources)과 스레드로 구성
- 스레드
프로세스 내에서 실제 작업을 수행
모든 프로세스는 최소한 하나의 스레드를 가지고 있다
프로세스 : 스레드 = 공장 : 일꾼
- 싱글 스레드 = 자원 + 스레드
- 멀티 스레드 = 자원 + 스레드 + 스레드 + ....
하나의 새로운 프로세스를 생성하는 것보다
하나의 새로운 스레드를 생성하는 것이 더 적은 비용이 든다.
2. 멀티스레드의 장단점
대부분의 프로그램이 멀티스레드로 작성되어 있다.
그러나, 멀티스레드 프로그래밍이 장점만 있는 것은 아니다.
장점 | - 시스템 자원을 보다 효율적으로 사용할 수 있다. - 사용자에 대한 응답성(responseness)이 향상된다. - 작업이 분리되어 코드가 간결해 진다. "여러 모로 좋다" |
단점 | - 동기화(synchronization)에 주의해야 한다. - 교착상태(dead-lock)가 발생하지 않도록 주의해야 한다. - 각 스레드가 효율적으로 고르게 실행될 수 있게 해야 한다. "프로그래밍할 때 고려해야 할 사항들이 많다." |
3. 스레드의 구현과 실행
1. Thread클래스를 상속
class MyThread extends Thread {
public void run() { //Thread클래스의 run()을 오버라이딩
/* 작업내용 */
}
}
2. Runnable인터페이스를 구현
class MyThread2 implements Runnable {
public void run() { // Runnable인터페이스의 추상메서드 run()을 구현
/* 작업내용 */
}
}
pulbic interface Runnable {
public abstract void run();
}
자바는 단일상속만을 허용하기 때문에 인터페이스인 Runnable을 사용하는 것이 더 유연하다.
Runnable은 run()이라는 메서드를 하나만 가지고 있는 인터페이스이다.
Thread를 상속 받든, Runnable을 구현하든 run()이란 메서드가 어떻게 동작할지 작성하면 된다.
사용하는 방법
// 1. Thread클래스 상속
MyThread t1 = new MyThread(); //스레드의 생성
t1.start(); //스레드의 실행
//2. Runnable인터페이스 구현
Runnable r = new MyThread2();
Thread t2 = new Thread(r); //Thread(Runnable r)
//Thread t2 = new Thread(new MyThread2());
t2.start();
⍟실습
class Ex13_1 {
public static void main(String args[]) {
ThreadEx1_1 t1 = new ThreadEx1_1();
Runnable r = new ThreadEx1_2();
Thread t2 = new Thread(r); // 생성자 Thread(Runnable target)
t1.start();
t2.start();
}
}
class ThreadEx1_1 extends Thread { //1. Thread클래스를 상속해서 스레드를 구현
public void run() { //스레드가 수행할 작업을 작성
for(int i=0; i < 5; i++) {
System.out.println(getName()); // 조상인 Thread의 getName()을 호출
}
}
}
class ThreadEx1_2 implements Runnable {//2. Runnable 인터페이스를 구현해서 스레드를 구현
public void run() {
for(int i=0; i < 5; i++) {
// Thread.currentThread() - 현재 실행중인 Thread를 반환한다.
System.out.println(Thread.currentThread().getName());
}
}
}
- Thread를 상속받으면 getName()을 사용할 때, this.getName()을 쓰거나 this.를 생략할 수 있다.
그러나 Runnable을 사용할 경우 Thread를 상속받은 것이 아니기 때문에 Thread.currentThread()를 이용해서 getName()을 사용한다.
위의 멀티스레드 코드가 어떻게 돌아가고 있는지 한눈에 알아보기 위해서 코드를 아래와 같이 수정하겠다.
(print값을 단순하게 0,1로 바꾸고, for문의 범위를 500까지 늘렸다)
class Ex13_1 {
public static void main(String args[]) {
ThreadEx1_1 t1 = new ThreadEx1_1();
Runnable r = new ThreadEx1_2();
Thread t2 = new Thread(r); // 생성자 Thread(Runnable target)
t1.start();
t2.start();
}
}
class ThreadEx1_1 extends Thread { //1. Thread클래스를 상속해서 스레드를 구현
public void run() { //스레드가 수행할 작업을 작성
for(int i=0; i < 500; i++) {
System.out.print(0); // 조상인 Thread의 getName()을 호출
}
}
}
class ThreadEx1_2 implements Runnable {//2. Runnable 인터페이스를 구현해서 스레드를 구현
public void run() {
for(int i=0; i < 500; i++) {
// Thread.currentThread() - 현재 실행중인 Thread를 반환한다.
System.out.print(1);
}
}
}
결과는 <그림1>과 같이 나오고 두 작업은 섞여서 진행이 되고 있다.
싱글스레드인 경우는 어떨까?
class Ex13_1 {
public static void main(String args[]) {
for(int i=0; i < 500; i++) {
System.out.print(0);
}
for(int i=0; i < 500; i++) {
System.out.print(1);
}
}
}
싱글 스레드는 우리가 흔히 작성하는 코드이고, 결과는 아래와 같이 절대 두 작업이 섞이지 않고 순차적으로 진행이 된다.
4. 스레드의 실행 - start()
- 스레드를 생성한 후에 start()를 호출해야 스레드가 작업을 시작한다.
ThreadEx1_1 t1 = new ThreadEx1_1(); //스레드 t1을 생성한다.
ThreadEx1_1 t2 = new ThreadEx1_1(); //스레드 t2를 생성한다.
t1.start(); //스레드 t1을 실행시킨다.
t2.start(); //스레드 t2를 실행시킨다.
start()는 실행가능한 상태가 되는 것이지 언제 실행할지는 os의 스케줄러가 결정한다. 따라서 t1이 먼저 적혀 있기는 하지만 무조건 t1이 t2보다 먼저 실행되는 것은 아니다.
즉, 1. start()해서 즉시 실행 X
2. 먼저 start()해서 먼저 실행되는 것은 X
JVM이 os에 독립적이라고는 말하지만 os에 종속적인 것도 몇 가지 있는데 그중 하나가 스레드이다.
5. start()와 run()
그런데 여기서 궁금한 점이 생길 것이다.
우리가 작성한 것은 run() 메서드인데 왜 start()를 호출하는 걸까?
이것을 알기 위해서 start()의 동작 방식에 대해 알아보자!
main에서 start를 호출하면, 2번처럼 start가 새로운 호출 스택을 생성한다.
그리고 3번처럼 새로운 호출 스택에 run을 올린다.
start의 할 일을 다 했으므로 4번처럼 start가 사라진다.
최종적인 결과 4번을 보면 각각의 스레드가 자신만의 호출스택을 가지고 실행한다는 것을 알 수 있다.
그래서 서로 독립적인 작업을 할 수 있는 것이다!
출처
'🎥Back > 자바의 정석' 카테고리의 다른 글
[JAVA의 정석]데몬 스레드, 스레드의 상태 (0) | 2024.02.29 |
---|---|
[JAVA의 정석]싱글 스레드와 멀티스레드, 스레드의 I/O 블락킹 (0) | 2024.02.29 |
[JAVA의 정석]Object클래스와 equals() (0) | 2024.02.29 |
[JAVA의 정석]hashCode(), toString() (0) | 2024.02.28 |
[JAVA의 정석]사용자 정의 예외 만들기, 예외 되던지기 (0) | 2024.02.28 |