JAVA/JAVA Basic

[ JAVA ] 스레드(Thread)의 생성

IT록흐 2021. 6. 20. 09:03
반응형

 

 

 

 

 

작은 테트리스 게임 안에서도

다양한 스레드가 사용된다.

 

 

프로그램의 실행을 담당하는 '메인스레드'

게임 실행을 담당하는 '게임스레드'

블럭의 움직임을 담당하는 '블럭스레드'

게임 안의 여러 효과음을 담당하는 '음악스레드'

게임의 이미지를 그리는 'awt, swing 데몬스레드'

 

 

이렇게 다양한 스레드들이 한 프로그램 안에서 독립적으로 활동한다. 그러므로 스레드들이 자원을 두고 서로 충돌되지 않도록 적절한 코딩을 할 필요가 있다.

 

스레드란 무엇인가?

 

CPU가 처리하는 일의 단위를

'프로세스'라고 한다.

 

그 프로세스는

여러 개의 스레드로 구성된다.

 

JVM은 스레드를

STACK영역에 할당한다.

 

 

 

 

메모리를 할당받은

각각의 스레드들은

자신만의 STACK 구조를 갖는다.

 

이렇게 자신만의

메모리를 갖는 작업단위는

'독립성'을 지닌다.

 

 

엑셀 프로그램이 종료되도, 한글 프로그램에는 영향이 없듯 말이다. 하지만 이것은 프로세스의 이야기다. 스레드는 프로세스 안에서 유기적으로 움직이기 때문에 한 스레드에서 예외가 발생하면 프로세스 전체가 종료된다. 스레드는 각자 움직이지만 같은 자원을 공유하기 때문에 서로의 관계를 잘 이해해야 한다.

 

스레드는 기본적로 메인스레드가 있다. 메인스레드가 다른스레드의 실행을 명령하면 멀티 태스킹이 시작된다. 그러나 메인스레드가 종료되었어도, 다른 스레드가 실행 중이면 프로그램이 종료되지 않으니 유의해야한다.

 

 

우선 스레드를 생성해보자.

 

스레드 생성

 

스레드를 만들려면 Thread 클래스를 이용해야한다. Thread 클래스는 JDK에서 제공하는 기본 라이브러리 안에 있다. Thread 클래스 안에는 run()가 존재하는데, 이는 Thread가 실행될 때 호출 될 메소드이다. 이 run()안에 Thread가 구체적으로 어떤 활동을 해야하는지에 대한 정보가 담겨 있어야 한다.

 

그러므로 이 안에 구체적인 정보를 담아야하는데 방법이 두 가지다.

 

1. Runnable 인터페이스

2. 자식클래스 생성하여 run() 오버라이딩

 

Runnable 인터페이스

출처 입력

Runnable 인터페이스는 Thread 클래스처럼 run()를 가진 인터페이스다. 인터페이스이기에 구현객체를 생성하여 사용해야 한다. Thread 클래스의 생성자는 Runnable 인터페이스의 구현객체를 매개값으로 받을 수 있다.

 

 

 

이렇게 Runnable의 구현객체를 매개값으로 받은 생성자로 Thread를 생성하여 실행하면 run()가 호출되는데, Thread 클래스의 run()이 아닌, Runnable의 구현객체의 run()이 호출된다. 그러므로 구현객체의 클래스의 run()을 잘 정의하면, 원하는대로 스레드를 실행시킬 수 있다.

 

 

< Runnable 인터페이스 구현객체 >

package createthread;

public class Task implements Runnable { 

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("저는 Runnable 인터페이스 구현객체의 run()입니다.");
	}
}

 

< Main >

package createthread;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Runnable task = new Task(); // 구현객체생성
		Thread thread = new Thread(task); // 스레드객체생성
		
		thread.start(); // 스레드의 run() 실행
	}
}

 

< 출력 결과 >

 

 

 

 

 

 

 

하지만 Runnable 인터페이스는 메소드로 run()밖에 갖고 있지 않다. 그러므로 구현개체는 오로지 run()만 사용가능하다. 그러니 굳이 구현객체를 클래스 파일로 생성할 필요가 없다. 익명의 구현객체를 사용해보자.

 

 

< Main >

package createthread;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Thread thread = new Thread(new Runnable() { //익명구현객체 생성
			@Override
			public void run() {
				System.out.println("저는 익명구현객체의 run입니다.");
				
			}
		});
		
		thread.start(); // 스레드의 run() 실행
	}

}

 

 

<출력결과>

대표사진 삭제

사진 설명을 입력하세요.

오로지 run()밖에 없는 단순한 구조인 Runnable 인터페이스는 따로 인터페이스의 구현 클래스를 만드는 수고를 하는 것보다도, 이런 익명의 구조로 더 자주 사용되어진다.

 

run() 재정의(오버라이딩)

출처 입력

단순한 기능을 하는 스레드를 생성할 때는, Runnable 인터페이스를 사용해주면 된다. 하지만 어떤 복잡한 일을 하는 객체가 독자적인 스레드 기능이 필요하다면 스레드를 '상속'시켜 사용해야한다.

 

< Thread 상속한 자식 클래스 >

package createthread;

public class Worker extends Thread{ // 스레드 상속
	
	private String objectName = null;
	
	public String getObjectName() {
		return objectName;
	}
	public void setObjectName(String objectName) {
		this.objectName = objectName;
	}

	@Override 
	public void run() { // Thread 클래스의 run() 오버라이딩
		System.out.println(getName() + " 객체를 실행합니다.");
	}
}

 

< Main >

package createthread;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Worker worker = new Worker();
		worker.setName("일꾼1");
		
		worker.start(); // 스레드 실행
	}
}

 

 

<출력 결과>

 

 

 

 


 

정리

 

1. 스레드는 독립적이지만 다른 스레드와 같은 자원을 공유하는 작업단위다.

2. 단순한 기능의 스레드는 Runnable 인터페이스를 사용한다.

3. 스레드기능이 필요한 객체는 스레드를 상속하여 사용한다.

 

 

 

 

 

 

 

 

 

반응형