관리 메뉴

나만을 위한 블로그

[JAVA] 쓰레드란? 본문

JAVA

[JAVA] 쓰레드란?

참깨빵위에참깨빵_ 2020. 10. 7. 01:32
728x90
반응형

Thread(쓰레드) : 실, (이야기 등의) 가닥 / 맥락, (실 등을) 꿰다

컴퓨팅에서의 쓰레드 : 어떤 프로그램(특히 프로세스) 내에서 실행되는 흐름의 단위. 일반적으로 한 프로그램은 하나의 쓰레드를 갖고 있지만 프로그램 환경에 따라 둘 이상의 쓰레드를 동시 실행할 수 있다. 이런 실행 방식을 멀티쓰레드라고 한다. (출처 - 한글 위키백과)

컴퓨팅에서의 쓰레드 - 실행 쓰레드는 일반적으로 OS의 일부인 스케줄러에 의해 독립적으로 관리할 수 있는 프로그래밍된 명령의 최소 순서다. 쓰레드 및 프로세스의 구현은 OS에 따라 다르지만, 대부분 쓰레드는 프로세스의 구성요소다. 한 프로세스 안에 여러 쓰레드가 있어서 동시 실행되고 메모리 같은 리소스를 공유할 수 있지만, 서로 다른 프로세스에선 리소스를 공유하지 않는다.... (출처 - 영문 위키백과)

 

쓰레드라는 말의 사전적 정의는 실이다. 컴퓨터에선 한 프로그램에서 실행되는 흐름의 단위를 쓰레드라고 한다.

이러한 흐름이 하나라면 싱글쓰레드, 여러 개라면 멀티쓰레드라고 하는 것으로 이해했다.

 

그럼 자바에서는 쓰레드를 어떻게 만들까? 자바에서는 쓰레드를 만드는 방법이 2가지 있다.

 

1. Thread 클래스 상속하기

2. Runnable 인터페이스 implement하기

 

각각의 방법을 써서 쓰레드를 구현해보겠다.

 

1) Thread 클래스 상속해서 쓰레드 만들기

 

public class Thread_class {

	public static void main(String[] args) {
		
		// 쓰레드 상속받은 클래스 객체화
		ThreadEx1 t1 = new ThreadEx1();
		// 쓰레드 시작
		t1.start();
		
	}
	
}

class ThreadEx1 extends Thread {
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("현재 숫자 : " + i);
		}
	}
}

이 소스코드를 실행하면 자동으로 0부터 4까지 1씩 숫자를 더해가며 출력한다.

쓰레드 클래스를 상속받은 ThreadEx1 클래스를 만들고 그 안에 run()을 만들고 for문을 돌며 0부터 1씩 더해서 출력하라는 코드를 입력했다.

그리고 메인 쓰레드가 실행되면 위의 행동 양식이 입력돼 있는 쓰레드가 실행된다.

 

 

2) Runnable 인터페이스 implement하기

 

 

public class Thread_class {

	public static void main(String[] args) {
		
		Runnable r = new ThreadEx1();
		Thread t1 = new Thread(r);
		t1.start();
		
	}
	
}

class ThreadEx1 implements Runnable {
	
	public void run()
	{
		for (int i = 0; i < 5; i++)
		{
			System.out.println("현재 숫자 : " + i);
		}
	}
	
}

 

위와 똑같은 결과를 출력하는 소스코드다. 차이점이라면 ThreadEx1 뒤에 붙는 키워드가 각각 다른 것, 메인 쓰레드에서 Runnable 인터페이스를 구현한 클래스의 객체를 ThreadEx1과 매핑해서 객체화한 다음, Thread 클래스의 생성자에 인자로 넣어서 실행하는 것이다.

그럼 공통점은 무엇일까? Thread를 상속받아서 쓰레드를 만들건, Runnable 인터페이스를 구현해서 쓰레드를 만들건 start()를 통해서 쓰레드를 시작한다는 것이다.

즉, start()를 호출해야만 쓰레드가 실행된다는 것이다. 이 말은 start() 전엔 쓰레드는 실행대기 상태에 있다가 자기 차례가 오면(start() 호출) 자신의 차례가 되야 실행된다는 것이다.

 

그럼 위에서 실행한 쓰레드를 다시 실행할 수 있을까? 확인을 위해 Runnable 인터페이스를 구현한 쓰레드를 연속으로 2번 실행시켜보자.

 

public class Thread_class {

	public static void main(String[] args) {
		
		Runnable r = new ThreadEx1();
		Thread t1 = new Thread(r);
		t1.start();
		t1.start();
		
	}
	
}

class ThreadEx1 implements Runnable {
	
	public void run()
	{
		for (int i = 0; i < 5; i++)
		{
			System.out.println("현재 숫자 : " + i);
		}
	}
	
}

start()를 2번 넣고 실행했다. 그러자 아래와 같은 화면이 나왔다.

 

 

올바르지 않은 쓰레드 상태로 인한 예외라는 뜻같다. 맨 마지막의 java:10을 누르면 2번째 t1.start()에 파란색 바가 생긴다.

 

 

이 에러가 뜨는 이유는 바로 쓰레드는 한 번 실행되고 종료되면 다시 실행할 수 없기 때문이다.

즉, A 쓰레드로 start()를 호출했다면 A 쓰레드는 다시 실행할 수 없다. 이것은 Thread를 상속받건 Runnable 인터페이스를 구현해 만들건 똑같다.

 

 

- start() vs run()

 

 

여기서 짚고 넘어갈 것이 있다. 구글에 쓰레드 예제를 검색하다 보면 run()으로도 쓰레드를 실행시키는 예제들이 종종 나온다.

그러면 run()과 start()는 각각 무엇이고, 둘은 무슨 차이가 있을까?

직접 만들어서 확인해보자.

 

public class ThreadRunVsStart {

	public static void main(String[] args) {
		
		ThreadRun t1 = new ThreadRun(1);
		ThreadRun t2 = new ThreadRun(2);
		ThreadRun t3 = new ThreadRun(3);
		
		t1.start();
		t2.start();
		t3.start();
		
	}

}

class ThreadRun extends Thread
{
	int name;

	public ThreadRun(int name) {
		this.name = name;
	}

	@Override
	public void run() {
		for (int i = 0; i < 3; i++)
		{
			System.out.println("Thread-" + name);
			try {
				Thread.sleep(100);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
}

먼저 Thread 클래스를 상속받는 방식으로 쓰레드를 만들었다. sleep()은 안에 쓰인 밀리초만큼 쓰레드를 정지시키고 실행시키는 메서드다. 그래서 0.1초씩 멈추면서 쓰레드가 실행된다.

위 코드의 실행 결과는 아래와 같다.

 

 

실행할 때마다 결과는 계속 바뀐다. 그럼 이번엔 run()으로 쓰레드를 실행시켜보자.

 

public class ThreadRunVsStart {

	public static void main(String[] args) {
		
		ThreadRun t1 = new ThreadRun(1);
		ThreadRun t2 = new ThreadRun(2);
		ThreadRun t3 = new ThreadRun(3);
		
		t1.run();
		t2.run();
		t3.run();
		
	}

}

class ThreadRun extends Thread
{
	int name;

	public ThreadRun(int name) {
		this.name = name;
	}

	@Override
	public void run() {
		for (int i = 0; i < 3; i++)
		{
			System.out.println("Thread-" + name);
			try {
				Thread.sleep(100);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
}

 

 

실행시킬 때마다 위 움짤과 같이 쓰레드들이 실행된다.

1번 쓰레드가 3번 실행되고 그 뒤에 2번 쓰레드가 3번, 3번 쓰레드가 3번 실행되고 프로그램은 종료된다.

즉, 각 쓰레드가 실행된 후 종료되어야 다음 쓰레드가 순차적으로 실행된다는 걸 알 수 있다.

반면 start()를 호출해서 쓰레드를 시작할 경우, 쓰레드가 병행실행되서 멀티쓰레드 방식으로 작동하기 때문에, 쓰레드 실행이 일관적으로 보이지 않는다는 걸 알 수 있다.

 

프로그램 내부 관점에서 보면, start()를 호출하면 새로운 쓰레드가 만들어지고 run() 내부 코드가 새로운 쓰레드에서 실행된다.

그러나 run()을 호출하면 새로운 쓰레드가 만들어지지 않고 run() 내부 코드가 실행된다.

반응형
Comments