프로세스(process)
- 프로세스(process)란 단순히 실행 중인 프로그램(program)
- 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것
- 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성
스레드(thread)
- 스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체
- 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행
- 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다
멀티스레드
- 멀티스레드 =multi thread =다중스레드
- 멀티스레드는 여러개의 스레드를 이용하는 프로그램이다
- 다중 스레드에서 각각의 스레드는 하나의 독립적인 프로세스처럼 작업 수행
작업관리자 > 성능 탭을 열어보면 프로세스와 스레드를 확인할 수 있다
멀티 스레드로 구현된 프로그램을 실행
- 하나의 프로세서는 한번에 스레드1개밖에 실행시키지 못한다
- 대신 일정한 시간 간격으로 수행해야하는 스레드를 전환한다
- 스레드를 전환할 때는 운영체제의 스케줄러의 기준에 따라 순서가 정해지게 된다
- 여러 스레드를 번갈아 처리하기 때문에 엄밀히 말하면 한번에 한가지를 처리하지만 동시에 작업하는 듯한 효과를 준다
- 이와 같은 방식을 시분할 방식 이라고 한다
멀티스레드 적용하기 위한 조건
- 병행성(concurrency) : 다수의 스레드 생성방법 존재
- 동기화(synchronization) : 작업이 방해받지 않고 각 스레드의 동기화 방법 존재
- 통신(communication) : 서로 다른 스레드가 정보를 교환할 수 있는 방법이 존재
스레드의 생성과 실행
생성하는 방법
- Runnable 인터페이스를 구현하는 방법
- Thread 클래스를 상속받는 방법
두 방법 모두 스레드를 통해 작업하고 싶은 내용을 run() 메소드에 작성하면 된다.
위 두가지 방법을 사용하여 스레드를 생성하고 실행하는 예제
class ThreadWithClass extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()); // 현재 실행 중인 스레드의 이름을 반환함.
try {
Thread.sleep(10); // 0.01초간 스레드를 멈춤.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadWithRunnable implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환함.
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread01 {
public static void main(String[] args){
ThreadWithClass thread1 = new ThreadWithClass(); // Thread 클래스를 상속받는 방법
Thread thread2 = new Thread(new ThreadWithRunnable()); // Runnable 인터페이스를 구현하는 방법
thread1.start(); // 스레드의 실행
thread2.start(); // 스레드의 실행
}
}
//결과
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
생성된 스레드가 서로 번갈아가며 실행되고 있는 것을 확인
Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없으므로,
일반적으로 Runnable 인터페이스를 구현하는 방법으로 스레드를 생성
스레드의 우선순위
- 자바에서 각 스레드는 우선순위(priority)에 관한 자신만의 필드를 가지고 있다
- 우선순위에 따라 특정 스레드가 더 많은 시간 동안 작업을 할 수 있도록 설정할 수 있다
필드 | 특징 |
static int MAX_PRIORITY | 스레드가 가질 수 있는 최대 우선순위를 명시함. |
static int MIN_PRIORITY | 스레드가 가질 수 있는 최소 우선순위를 명시함. |
static int NORM_PRIORITY | 스레드가 생성될 때 가지는 기본 우선순위를 명시함. |
getPriority()와 setPriority() 메소드를 통해 스레드의 우선순위를 반환하거나 변경할 수 있다.
스레드의 우선순위가 가질 수 있는 범위는 1부터 10까지이며, 숫자가 높을수록 우선순위 또한 높아진다.
- 스레드의 우선순위는 비례적인 절댓값이 아닌 어디까지나 상대적인 값
- 우선순위가 10인 스레드가 우선순위가 1인 스레드보다 10배 더 빨리 수행되는 것은 아니다.
- 단지 우선순위가 10인 스레드는 우선순위가 1인 스레드보다 좀 더 많이 실행 큐에 포함되어,
좀 더 많은 작업 시간을 할당받을 뿐
class ThreadWithRunnable implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환함.
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread02 {
public static void main(String[] args){
Thread thread1 = new Thread(new ThreadWithRunnable());
Thread thread2 = new Thread(new ThreadWithRunnable());
① thread2.setPriority(10); // Thread-1의 우선순위를 10으로 변경함.
② thread1.start(); // Thread-0 실행
③ thread2.start(); // Thread-1 실행
System.out.println(thread1.getPriority());
System.out.println(thread2.getPriority());
}
}
//결과
5
10
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
main() 메소드를 실행하는 스레드의 우선순위는 언제나 5이므로,
main() 메소드 내에서 생성된 스레드 Thread-0의 우선순위는 5로 설정되는 것을 확인할 수 있다.
만약 ①번 라인이 존재하지 않는다면, Thread-0이 먼저 실행되고, Thread-1이 나중에 실행
하지만 ①번 라인에서 Thread-1의 우선순위를 10으로 변경했기 때문에,
Thread-1이 나중에 실행됐더라도 우선순위가 Thread-0보다 높아 먼저 실행되는 것
스레드의 상태
상태 |
구분 | 내용 |
초기 상태 | NEW | 스레드 객체를 생성한 상태 |
실행 가능 | RUNNABLE | start()메서드로 객체를 호출하여 실행할 수 있는 상태 |
실행 중 | RUNNING | 실행가능한 스레드 중에서 스케줄러가 선택하여 스레드가 실행 중인 상태. CPU에 의해 코드가 한줄씩 실행된다. CPU는 n개의 스레드 중 1개밖에 처리할 수 없으며 n-1개의 스레드는 대기상태가 된다 |
대기상태 | WATING | 실행 중지. 다른 스레드의 통보(notify)를 기다리는 상태 |
TIMED_WATING | 실행 중지. 주어진 시간동안 대기상태 | |
BLOCKED | 다른 스레드에서 락(lock)을 획득한 이유로 락이 걸린 상태 | |
종료 | TERMINATED | 스레드가 종료된 상태. 한번 종료되면 다시 시작될 수 없다. |
스레드 상태는 getState() 스태틱 메서드를 이용해 확인할 수 있다.
스레드 동기화(synchronized)
멀티스레드 프로그램은 스레드끼리 객체를 공유해 작업하는 경우가 있다. synchronized 키워드를 사용하면 특정 스레드가 사용중인 객체에 다른 스레드가 접근할 수 없게 되어 동기화가 보장된다. synchronized 키워드는 인스턴스, 정적 메서드, 특정 코드 영역 등에 붙일 수 있다.
- 임계영역(critical section)이란 스레드 1개만 실행하거나 접근이 가능한 자원 및 코드의 범위를 말한다.
- 락(lock)이란 공유객체에 여러 스레드가 접근하지 못하게 하는 과정이다. 락은 모든 객체가 메모리 힙 영역에 생성될 때 자동으로 할당된다.
스레드 동기화 과정
특정 스레드 A가 실행중(Running)에 synchronized 선언된 블록을 만나게 되면 object's Lock Pool 로 이동하고 락(lock)을 획득한다. 다른 스레드는 Running 중 Blocked 되어 실행가능한(Runnable) 상태가 된다.
특정 스레드 A의 synchronized 블록 실행이 종료된다면 A는 락을 해제한다.
A가 락을 해제하면 다른 스레드 B가 실행가능한(Runnable)상태에서 실행(Running)을 하게 된다. 만약 스레드 B가 synchronized 선언된 블록을 만나면 B가 object's Lock Pool 로 이동하고 락(lock)을 획득한다. 이후 과정은 앞과 동일
'Java > Java' 카테고리의 다른 글
[Java] 예외 (1) | 2022.12.07 |
---|---|
[Java] 타입 변환과 다형성 (0) | 2022.12.06 |
[Java] 인터페이스(Interface) (0) | 2022.12.05 |
[Java] 추상 클래스, 메소드 (0) | 2022.12.02 |
[Java]타입 변환과 다형성 (0) | 2022.11.30 |
댓글