실행 중인 프로그램 = 데이터 + 자원 (메모리) + 쓰레드
✅ 실행 : 프로그램이 메모리에 적재되어 CPU를 할당받아 기계어 명령을 수행하는 상태
프로세스의 자원을 이용하여 실제로 작업을 수행하는 것
둘 이상의 쓰레드가 자원을 공유하며 작업을 수행
ex) 카톡 채팅창에서 사진을 다운로드 받으면서 채팅 가능 = 채팅창 (자원) + 다운로드 (쓰레드 1) + 채팅 (쓰레드 2)
멀티 쓰레딩 환경에서는 각 쓰레드가 번갈아가며 수행되는데, 이 과정에서 쓰레드 간 작업 전환 시간이 추가로 소요되며, 작업을 수행하지 않는 쓰레드에게는 대기 시간이 발생하게 된다.
⭐ 서로 다른 자원을 사용하는 작업들은 멀티 쓰레드로 처리하는 것이 효율적이다.
Thread
클래스 상속 orRunnable
인터페이스 구현 ➡️run()
구현 필수!
쓰레드 생성
자식 클래스 : 부모인 Thread
의 getName()
호출 가능
구현 클래스 : Thread
의 static 메서드인 currentThread()
를 통해 현재 실행 중인 쓰레드의 참조를 얻어와야만 getName()
호출 가능
class Thread1 extends Thread { // 클래스 상속
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(i + " : " + getName()); // Thread.getName();
}
}
}
class Thread2 implements Runnable { // 인터페이스 구현
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(i + " : " + Thread.currentThread().getName());
}
}
}
인스턴스 생성
자식 클래스 : 클래스 인스턴스 생성
구현 클래스 : 클래스 인스턴스 생성 후, Thread
클래스의 생성자 파라미터로 제공
// 클래스 상속
Thread1 t1 = new Thread1();
// 인터페이스 구현
Runnable r = new Thread2();
Thread t2 = new Thread(r);
Thread t2 = new Thread(new Thread2());
start()
메서드 호출 : 각 쓰레드 당 한 번만 호출 가능!
t1.start();
t2.start();
// t1.start();
// IllegalThreadStateException : 이미 실행 중인 쓰레드를 호출한 경우
// 쓰레드를 다시 생성한 경우에는 가능!
t1 = new Thread1();
t1.start();
: 쓰레드들이 짧은 시간 간격으로 번갈아가면서 실행됨 (실행 순서는 OS의 스케줄러가 결정)
start()
& run()
run()
: 쓰레드 클래스에 생성된 메서드 호출start()
: 새로운 호출 스택 생성 ⭐
: main()
에서 start()
를 호출하면 호출 스택에 start()
가 쌓이면서 가장 위에 있는 start()
를 실행한다. 해당 메서드는 새로운 쓰레드를 생성하는데, 이때 해당 쓰레드에서 작업을 처리하기 위한 호출 스택이 생성된다. 즉, main()
이 실행되는 쓰레드와 start()
에 의해 새롭게 생성된 쓰레드가 번갈아가며 작업을 수행하는 것이다!
: start()
이후 수행할 작업이 없어 main()
메서드가 종료되면서 첫번째 쓰레드는 작업을 마쳤지만, 두번째 쓰레드에서 아직 run()
을 실행중이기 때문에 프로그램이 종료되지 않는다. 즉, 모든 사용자 쓰레드의 실행이 종료되어야만 프로그램도 종료된다.
ex) throwException()
: 예외를 발생시켜, 예외가 발생한 호출 스택을 출력
class Thread1 extends Thread {
@Override
public void run() {
throwException();
}
public void throwException() {
try {
throw new Exception();
} catch(Exception e) {
e.printStackTrace();
}
}
}
run()
: 호출 스택에 main()
포함 ➡️ main()
이 실행된 쓰레드에 run()
이 호출된 형태public class Test{
public static void main(String[] args) {
Thread t1 = new Thread1();
t1.run();
}
}
start()
: 호출 스택에 쓰레드 관련 메서드만 존재 ➡️ 새로운 쓰레드 및 호출 스택 생성public class Test{
public static void main(String[] args) {
Thread t1 = new Thread1();
t1.start();
}
}
한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것
: 멀티 쓰레드 프로세스에서는 모든 쓰레드가 같은 자원을 공유해서 사용한다.
예를 들어, 쓰레드1에서 자원1을 가지고 작업을 수행하다가 쓰레드2로 제어권이 넘어간 경우, 쓰레드2에서 쓰레드1에서 사용하던 자원1의 값을 변경하면 이후 쓰레드1의 작업 수행 결과가 의도했던 결과와 다를 수 있다.
이와 같은 경우를 방지하기 위해, 공유 자원을 사용하는 코드 영역을 임계 영역(critical section)으로 지정하고, 공유 자원의 lock을 획득한 단 하나의 쓰레드만 임계 영역의 코드를 수행할 수 있게 해야 한다.