# Thread 란?
오늘은 자바의 기본 개념인 스레드에 대해 공부해보자!
먼저 메모리를 할당받아 실행 중인 프로그램을 우리는 프로세스 라고 부른다.
보통 한 개의 프로세스는 한 가지의 일을 하지만, 스레드를 이용하면 한 프로세스 안에서 두 가지 또는 그 이상의 일을 동시에 진행할 수 있다.
# Thread 생성
스레드는 아래 두가지 방법으로 생성이 가능하지만, 인스턴스 생성 방법에 차이가 있다.
public class Sample implements Runnable {
@Override
public void run() { // Thread 를 상속하려면 run 메소드를 구현해야 한다
// 코드
}
}
먼저 Runnable 인터페이스로 구현한 경우, 해당 클래스를 인스턴스화 해서 Thread 생성자에 argument 로 넘겨줘야 하고, run() 호출 시, Runnable 인터페이스에서 구현한 run() 이 호출되므로 오버라이딩을 하지 않아도 된다는 장점이 있다.
public class Sample extends Thread {
@Override
public void run() {
// 코드
}
}
반면 위와 같이 Thread 클래스를 상속받은 경우에는, 클래스 자체를 스레드로 사용할 수 있다.
아래 예제의 경우, Thread 클래스를 상속받은 2번째 경우로 진행하겠다.
이제 이렇게 만든 스레드를 직접 사용해보자.
public class Sample extends Thread {
public void run() {
System.out.println("Running...");
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.start(); // 스레드 실행
}
}
이렇게 Sample 클래스에 Thread 클래스를 상속하여 run 메소드를 구현하게 되면 'Running...' 이라는 문제가 출력, 즉 sample 객체의 run 메소드가 실행된다.
# Thread 예제
public class Sample extends Thread {
int i;
public Sample(int i) {this.i = i;}
public void run() {
System.out.println(this.i + ". 스레드 시작");
try {
Thread.sleep(1000); // 시작점과 종료지점의 차이를 주기 위해 1초의 간격 생성
} catch (Exception e) {
}
System.out.println(this.i + ". 스레드 종료");
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { // 스레드 10개 생성
Thread t = new Sample(i);
t.start();
}
}
}
먼저 스레드의 작동을 정확하게 보기 위해 i 로 순번을 부여하였고,
그 결과는 다음과 같다. (동일하지 않을 수 있음!)
3. 스레드 시작
9. 스레드 시작
8. 스레드 시작
7. 스레드 시작
1. 스레드 시작
6. 스레드 시작
2. 스레드 시작
5. 스레드 시작
4. 스레드 시작
0. 스레드 시작
8. 스레드 종료
5. 스레드 종료
6. 스레드 종료
7. 스레드 종료
9. 스레드 종료
3. 스레드 종료
1. 스레드 종료
4. 스레드 종료
0. 스레드 종료
2. 스레드 종료
for (int i = 0; i < 10 ; i++) 를 통해 총 0~9, 10 개의 스레드를 만들어 실행했는데, 스레드가 순서대로 실행되지 않은 것을 볼 수 있다.
즉. 스레드는 만드는 순서에 상관 없이 거의 동시에 실행된다.
이것이 바로 위에 결과가 동일하지 않을 수 있는 이유이다!
# Thread 실행 제어
스레드의 상태는 다음과 같이 여섯가지가 있다.
- NEW : 스레드 생성뒤, start()가 호출되지 않은 상태
- RUNNABLE : 실행 중/실행 가능 상태
- BLOCKED : 일시정지된 상태
- WAITING : 다른 스레드가 notify(), notifyAll() 을 불러주기를 기다리고 있는 상태 (동기화) - 실행 불가능 상태
- TIME_WAITING : 스레드가 sleep(n) 호출로 잠을 자고 있는 상태
- TERMINATED : 스레드 작업이 끝난 상태
# Thread 동기화 - synchronized
자바는 한 순간에 하나의 스레드만 실행할 수 있는 synchronized method 를 제공한다.
다음과 같이 메소드 앞에 synchronized 를 붙여 synchronized method 로 지정하게 되면, 한 스레드가 synchronized method 를 수행중이면, 다른 스레드는 대기하므로 충돌을 막을 수 있다.
public synchronized void deposite(int i){ // 입금
int m = money;
try{ Thread.sleep(1000); } catch (Exception e){
}
money = m + i;
System.out.println("입금");
}
public synchronized void withdraw(int i){ // 출금
int m = money;
try{ Thread.sleep(2000); } catch (Exception e){
}
money = m - i;
System.out.println("출금");
}
# Thread 동기화 - wait(), notify()
- wait() : 스레드가 lock을 가지고 있으면, lock 권한 반납 후 대기
- notify() : 대기 상태인 스레드에게 다시 lock 권한을 부여 (실행 가능)
메소드를 통해서도 동기화 처리가 가능한데, 다음 예제를 확인해보자
public class Sample extends Thread {
private int breadCount = 0;
private final Object lock = new Object();
public synchronized void bake(){
if (breadCount >= 10){
try {
System.out.println("빵 생산 초과");
wait(); // Thread를 Not Runnable 상태로 전환
} catch (Exception e) {}
}
breadCount++; // 빵 생산
System.out.println("빵 생산 - 총 " + breadCount + " 개");
notify(); // Thread를 Runnable 상태로 전환
}
public synchronized void buy(){
if (breadCount < 1){
try {
System.out.println("빵 품절");
wait();
} catch (Exception e) {}
}
breadCount--;
System.out.println("빵 구매 - 총 " + breadCount + " 개");
notify();
}
public static void main(String[] args) {
Sample sample = new Sample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
sample.bake();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
sample.buy();
}
});
thread1.start();
thread2.start();
}
}
아래는 실행 예시이다.
빵 생산 - 총 1 개
빵 생산 - 총 2 개
빵 생산 - 총 3 개
빵 생산 - 총 4 개
빵 생산 - 총 5 개
빵 생산 - 총 6 개
빵 생산 - 총 7 개
빵 생산 - 총 8 개
빵 생산 - 총 9 개
빵 구매 - 총 8 개
빵 구매 - 총 7 개
빵 구매 - 총 6 개
빵 구매 - 총 5 개
빵 구매 - 총 4 개
빵 구매 - 총 3 개
빵 구매 - 총 2 개
빵 구매 - 총 1 개
빵 구매 - 총 0 개
빵 품절
빵 생산 - 총 1 개
빵 구매 - 총 0 개
이렇게 조건이 만족하지 않을 시 (빵 품절 시) wait() 상태가 되게 되고, 만족 시 notify() 를 수행하게 되는 것이다.
'𝑷𝒓𝒐𝒈𝒓𝒂𝒎𝒎𝒊𝒏𝒈 > 𝐽𝐴𝑉𝐴' 카테고리의 다른 글
[JAVA] Collection - List (ArrayList) (0) | 2022.11.21 |
---|---|
[디자인패턴] 프록시(Proxy) 패턴 (0) | 2022.11.19 |
[디자인패턴] 싱글톤(Singleton) 패턴 (0) | 2022.11.19 |
[JAVA] 자바 queue (큐) 클래스 (0) | 2022.02.13 |
[JAVA] 자바 stack (스택) 클래스 (0) | 2022.02.10 |