IT/development

[Java] 자바 Thread(feat. Thread , Runnable, join)

알 수 없는 사용자 2022. 11. 23. 06:54
반응형

image source:https://unsplash.com/s/photos/java

 

Java Thread 😏

image source:https://unsplash.com/s/photos/thread

스레드란 하나의 프로세스 안에서 독립적으로 실행되는 작업 단위를 뜻함

모든 프로세스에는 한 개 이상의 스레드가 존재하며 작업을 수행함

두개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스라고 함

※ 프로세스 : 실행중인 프로그램, 운영체제에 의해 메모리 공간을 할당 받아 동작중인 것을 뜻함

이런 프로세스는 프로그램에 사용되는 데이터, 메모리 등의 자원, 스레드로 구성됨

 

 

Thread의 생성주기 😊

Runnable 상태 : 

스레드 실행 전 준비상태

Running 상태 :

스케줄러에 의해 선택된 스레드가 실행되는 단계

Blocked 상태 : 

스레드가 작업을 완수하지 못하고 잠시 작업을 멈추는 단계

 

 

Thread의 생명주기 😶

Runnable (준비상태)

스레드 실행되기 위한 준비단계, CPU 미점유 중이고 실행하기 위해 대기하고 있는 상태

 

Running (실행상태)

CPU 점유하여 실행 중인 상태이고 run() 메서드는 JVM만이 호출 가능
준비상태의 여러 스레드 중 우선 순위 스레드 결정 될 시 JVM이 자동으로 run() 메서드 호출 -> 스레드가 Running 상태로 진입

 

Dead (종료상태)

실행상태에서 스레드가 모두 실행되고 난 후 완료 상태(done상태라고도 함)

 

Blocked (지연 상태)

CPU 점유권을 상실한 상태, 후에 특정 메서드 실행 시켜 준비상태로 전환,
wait() 메서드에 의해 Blocked 상태가 된 스레드는 nofity() 메서드 호출 될 시 준비상태로 감, 
sleep(시간) 메서드에 의해 Blocked 상태가 된 스레드는 지정된 시간이 지나면 준비 상태로 감

 

 

Thread 생성/실행 😗

스레드 생성/실행 방법은 2가지가 있음

둘 다 run() 메소드에 작업할 내용을 작성하면 된다.

new Thread()로 객체 생성 후 start() 실행 시 내가 작성한 run()에 작성된 내용이 실행된다.

 

1.Thread class 상속

스레드 클래스를 상속 받을 경우 다른 클래스를 상속 받지 못하기에 일반적으로 Runnable 인터페이스를 구현하는 방법을 많이 사용함

// Thread class 직접 상속 예제
package java_test.dev.java;

public class Thread1 extends Thread {
	private int[] cnt;

	public Thread1(String threadname) {
		super(threadname);
		cnt = new int[10];

		for (int start = 0; start < cnt.length; start++) {
			cnt[start] = start;
		}
	}
	// 스레드 작업 내용
	public void run() {
		for (int start : cnt) {
			try {
				// 1초 대기
				Thread.sleep(1000);
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			}

			System.out.println("스레드이름:" + currentThread().getName());
			System.out.println("temp value :" + start);
		}
	}
	// 메인 호출
	public static void main(String[] args) {
		Thread1 t1 = new Thread1("첫번째");
		t1.start();
	}
}

 

 

2.Runnable 인터페이스 구현

추상 메서드인 run() 1개만 가지고 있는 함수형 인터페이스이다.

// Runnable 인터페이스 구현 예제
package java_test.dev.java;

public class Thread2 implements Runnable {
	private int[] temp;

	public Thread2() {
		temp = new int[10];

		for (int start = 0; start < temp.length; start++) {
			temp[start] = start;
		}
	}
	// 스레드 작업 내용(Runnable 구현 시 강제 오버라이드)
	@Override
	public void run() {
		for (int start : temp) {
			try {
				// 1초 대기
				Thread.sleep(1000);
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			}
			System.out.println("스레드이름:" + Thread.currentThread().getName());
			System.out.println("temp value :" + start);
		}
	}
	public static void main(String[] args) {
		Thread2 t2 = new Thread2();
		Thread t = new Thread(t2, "두번 째");
		t.start();
	}
}

 

 

멀티 스레드

여러 스레드를 동시에 실행시키는 기법

// 멀티 스레드 예제
package java_test.dev.java;

class ATM implements Runnable {
    private long depositeMoney = 10000;
    
    // 스레드에서 할 작업
    public void run() {
    // multi thread에서 데이터 공유
	synchronized (this) {
			
	    for (int i = 0; i < 10; i++) {
	    		// 준비상태로 변함
	           notify();
		try {
			// wait()으로 인해 blocked 상태로 변함
			wait();
			// 1초 대기
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 잔액이 없어지면 반복문 빠져나옴
		if (getDepositeMoney() <= 0)
			break;
		withDraw(1000);
	    }
	}
}
// 1000원씩 차감
public void withDraw(long howMuch) {
	// 잔액이 있으면
	if (getDepositeMoney() > 0) {
		depositeMoney -= howMuch;
		System.out.print(Thread.currentThread().getName() + " , ");
		System.out.printf("잔액 : %,d 원 %n", getDepositeMoney());
	} else {
		System.out.print(Thread.currentThread().getName() + " , ");
		System.out.println("잔액이 부족합니다.");
	}
}
// 잔액 조회
public long getDepositeMoney() {
	return depositeMoney;
    }
}

public class MultiThread {
	// main 실행
    public static void main(String[] args) {
	ATM atm = new ATM();
	// multi thread 생성
	Thread mother = new Thread(atm, "mother");
	Thread son = new Thread(atm, "son");
	// multi thread 실행
	mother.start();
	son.start();
    }
}

 

synchronized 때문에 2개의 스레드에서 10,000원이 같이 사용된 걸 확인할 수 있음

 

Join(스레드 대기)

스레드는 보통 다른 스레드와 독립적으로 실행되지만 다른 스레드가 종료될 때까지 기다려야 되는 경우가 발생할 수 있음

ex) A스레드가 종료된 후 B 스레드가 실행되어야 될 경우

이 때 join() 사용

package java_test.dev.java;

class ThreadJoin implements Runnable{

	@Override
	// 스레드 구현
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("쓰레드1단계");
		thread2();
	}
	
	public void thread2() {
		System.out.println("쓰레드2단계");
		thread3();
	}

	public void thread3() {
		System.out.println("쓰레드3단계");
	}
	
	public static void main(String[] args) {
		// main thread 시작 메시지 출력
		System.out.println(Thread.currentThread().getName() + " start");
		Runnable r = new ThreadJoin();
		Thread thread = new Thread(r);
		// 스레드 실행
		thread.start();
		// main thread 종료 메시지 출력 
		System.out.println(Thread.currentThread().getName() + " end");
		System.out.println("===========================================================");
	}
}

 

위 결과를 보면 main thread가 종료되고도 다른 스레드가 실행되는 걸 알 수 있다.

이를 스레드가 종료된 후 메인을 종료시키도록 변경하려면 아래처럼 변경하면 된다.

 

public static void main(String[] args) {
		// main thread 시작 메시지 출력
		System.out.println(Thread.currentThread().getName() + " start");
		Runnable r = new ThreadJoin();
		Thread thread = new Thread(r);
		// 스레드 실행
		thread.start();
        
		// thread.join() 추가(예외처리 필수)
		try {
			// thread 종료까지 기다림
			thread.join();
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		// main thread 종료 메시지 출력 
		System.out.println(Thread.currentThread().getName() + " end");
		System.out.println("===========================================================");
	}

 

메인 시작 후 스레드 종료 후 메인이 종료된 걸 볼 수 있다.

 

참조 : 코딩팩토리님 블로그, 2018.12.17, https://coding-factory.tistory.com/279,

          TCP School, http://www.tcpschool.com/java/java_thread_concept

            코딩벌레님 블로그, https://dpdpwl.tistory.com/13

반응형