728x90
반응형
SMALL

멀티 스레드 개념

- 운영체제는 실행 중인 프로그램을 프로세스로 관리한다. ( 멀티태스킹: 두가지 이상의 작업을 동시에 처리하는 것)

- OS는 멀티 프로세스를 생성해서 처리.

- 하나의 프로세스가 두 가지 이상의 작업을 처리할 수 있는 이유는 멀티 스레드가 있기 때문( 스레드 : 코드의 실행 흐름)

 

 

메인 스레드

- 모든 자바 프로그램은 메인 스레드가 main()메소드를 실행하면서 시작된다.

- 메인 스레드는 main()의 첫 코드부터 순차적 실행하며 마지막 코드를 실행하거나 return문을 만나면 종료한다.

작업 스레드 생성과 실행

 

작업 스레드 생성과 실행

- 멀티 스레드로 실행하는 프로그램을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 생성해야한다.

- 자바에서는 메인 스레드가 반드시 존재하기 때문에 이외 추가적인 작업 수만큼 스레드 생성하면 된다. 또 스레드도 객체로 관리하므로 클래스가 필요하다.

 

Thread 클래스로 직접 생성

java.lang 패키지에 있는 Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 다음과 같이 Runnable 구현 객체를 매개값으로 갖는 생성자를 호출하면 된다.

Thread thread = new Thread(Runnable target);

Runnable은 스레드가 작업을 실행할 때 사용하는 인터페이스다. 이 인터페이스에는 run()메소드가 정의되어있다.

구현 클래스는 run()을 재정의해서 스레드가 실행할 코드를 갖고 있어야 한다.

class MyRunnable implements Runnable{
	@Override
    public void run(){
    	//실행할 코드
    }
}

사용 방법

Runnable myRunnable = new MyRunnable();

Thread thread = new Thread(myRunnable);

thread.start();
Thread thread = new Thread(new Runnable(){
	@Override
    public void run(){
    	//스레드가 실행할 코드
    }
});

thread.start();

 

Thread 자식 클래스로 생성

또 다른 방법은 Thread의 자식 객체로 만드는 것이다. Thread를 상속하여 run()메소드를 재정의해 스레드가 실행할 코드를 작성하면 된다.

하지만 이 방법은 Thread를 extends로 상속받기 때문에 다른 부모를 상속받지 못한다는 단점이 있다.

public class WorkerTread extends Thread{
	@Override
    public void run(){
    	//스레드가 실행할 코드
    }
}

//스레드객체 생성
Thread thread = new WorkerTread();
thread.start();

 

스레드 이름

스레드는 각자 자신의 이름을 갖고 있다. 메인 스레드는 'main'이라는 이름을 갖고 있고, 작업 스레드는 자동적으로 'Thread-n'이라는 이름은 가진다. 만약 다른 이름으로 설정하고 싶으면 Thread클래스의 setName() 메소드를 사용한다.

thread.setName("스레드이름");

Thread thread = Thread.currendThread();
System.out.println(thread.getName());

 

스레드 상태

1. 스레드 객체를 생성(NEW)하고, start() 메소드를 호출하면 RUNNABLE상태가 됨. =>실행 대기

2. 실행 대기하는 스레드는 CPU 스케쥴링에 따라 CPU를 점유하고 run()메소드가 실행하면 RUNNING상태가 됨

3. 실행 스레드는 run()메소드의 코드를 모두 실행하기 전에 스케줄링에 의해 RUNNABLE상태로 돌아갈 수 있으며, 그 때 다른 스레드가 실행 상태가 됨

4. 실행상태에서 run() 메소드가 종료되면 더 이상 실행할 코드가 없기 때문에 스레드의 실행은 멈추며 TERMIATED가 됨.

 

주어진 시간 동안 일시 정지

실행 중인 스레드를 일정 시간 멈추게 하고 싶다면 Thread 클래스의 정적 메소드인 sleep()을 이용

try{
	Thread.speep(1000) // 1초 일시정지
} catch(InterruptedException e){
	//interrupt() 메소드가 호출되면 실행
}

 

다른 스레드의 종료를 기다림

스레드는 다른 스레드와 독립적으로 실행하지만 다른 스레드가 종료될 때까지 기다렸다가 실행해야 하는 경우에 join()메소드를 사용한다.

//<Thread A>

threadB.start(); //threadB 시작
threadB.join(); //threadA 일시 정지. =>B가 끝날 때까지 A는 실행하지 않음.

//<Thread B>
run(){

}

 

 

스레드 동기화

멀티 스레드는 하나의 객체를 공유해서 사용할 수도 있다. 한 객체를 여러 스레드가 접근해서 데이터를 변경하거나 가져올 때 원하지 않는 결과가 발생할 수도 있다.

메소드에 synchrosized키워드를 추가해 한 스레드가 끝날 때까지 flag를 가져가지 못하도록 할 수 있다.

 

스레드 안전 종료

스레드는 자신의 run()메소드가 모두 실행되면 자동적으로 종료되지만, 안전하게 사용하던 리소스들을 정리하고 run()메소드를 빨리 종료하는 방법은 조건 이용 방법과 interrupt()메소드 이용 방법이 있다.

 

조건 이용

public class XXXThread extends Thread{
	private boolean stop;
    
    public void run(){
		while(!stop){
			//스레드가 반복 실행하는 코드;
		}
        //스레드가 사용한 리소스 정리
	}
}

 

interrupt()메소드 이용

interrupt() 메소드는 스레드가 일시 정지 상태에 있을 때 InterruptedException 예외를 발생시켜 run()메소드를 정상 종료시킬 수 있다.

XThread thead = new XThread();
thread.start; //---1
...
thread.interrupt(); //---2


public void run(){ //---1로 인해 진행
	try{
    	while(true){
			...
			Thread.sleep(1); //---2로 인해 일시정지
		}
    }catch(InterruptedException e){ //---일시정지로 인해 예외 발생
		
	}
    //스레드가 사용한 리소스 정리
}

 

데몬 스레드

데몬 스레드는 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드이다. 주 스레드가 종료되면 데몬 스레드도 따라서 자동 종료된다.

스레드를 데몬으로 만들기 위해 주 스레드가 데몬이 될 스레드의 setDaemon(true)를 호출한다.

public static void main(String[] args){
	AutoSaveThread thread = new AutoSaveThread();
    thread.setDaemon(true);
    thread.start();
}

=>AutoSaveThread는 데몬 스레드가 된다.

 

스레드풀

병렬 작업 처리가 많아지면 스레드 개수가 폭증하여 CPU가 바빠지고 메모리 사용량이 늘어난다. 따라서 어플리케이션 성능이 저하되기 때문에 스레드 폭증을 막기 위한 스레드풀을 사용하는 것이 좋다.

스레드의 개수를 제한하여 큐에 들어오는 작업들을 스레드가 하나씩 맡아 처리하는 방식이다.

 

스레드풀 생성

메도드명(매개변수) 초기 수 코어 수 최대 수
newCachedThreadPool() 0 0 Integer.MAX_VALUE
newFixedThreadPool(int nThreads) 0 생성된 수 nThreads

 

ExecutorService executorService = Executors.newCachedThreadPool();
// 초기 수와 코어 수는 0개이고, 작업수가 많아지면 새 스레드를 생성시켜 작업을 처리한다.

ExecutorService executorService = Executors.newFixedThreadPool(5);
// 초기 수는 0개이고, 작업 수가 많아지면 최대 5개까지 스레드를 생성시켜 작업을 처리한다.

ExecutorService threadPool = new ThreadPoolExecutor(
	3, //코어 스레드 개수
    100, //최대 스레드 개수
    120L, //놀고 있는 시간
    TimeUnit.SECONDS, //놀고 있는 시간의 단위
    new SynchronousQueue<Runnable>() //작업 큐
);

 

스레드풀 종료

스레드풀의 스레드는 데몬 스렏가 아니기 때문에 main스레드가 종료되더라도 계속 실행 상태로 남아있다. 두 가지 중 하나의 메소드를 실행해야한다.

리턴 타입 메소드명(매개변수) 설명
void shutdown() 현재 처리 중인 작업뿐만 아니라 작업 큐에 대기하고 있는 모든 작업을 처리한 뒤에 스레드풀을 종료시킨다.
List<Runnable> shutdownNow() 현재 작업 처리 중인 스레드를 interrupt해서 작업을 중지시키고 스레드풀을 종료시킨다. 리턴값은 작업 큐에 있는 미처리된 작업(Runnable)의 목록이다.
728x90
반응형
LIST

'IT > JAVA' 카테고리의 다른 글

[JAVA] #16 람다식  (0) 2023.04.27
[JAVA] #15 컬렉션 자료구조  (0) 2023.04.27
[JAVA] #13 제네릭  (0) 2023.04.24
[JAVA] #12 java.base 모듈  (0) 2023.04.20
[JAVA] #11 예외 처리  (0) 2023.04.19

+ Recent posts