개발/JAVA

[JAVA]자바 Thread 스레드

윤_ve 2021. 3. 10. 22:23

이번엔 스레드에 관해서 알아 보겠다.

 

스레드를 알려면 프로세스와 멀티태스킹의 개념을 알아야 한다.

말만 보면 어느정도 다 들어본 말이라 무슨 의미인줄 알수 있을것이다.


프로세스

저장장치에서 실행명령을 기다리는 명령어들의 집합을 의미하고 쉽게 생각하면 자신만을 할당 받는 '실행중인 프로그램'이란 뜻이다.

 

멀티 태스킹

우리가 평소에 당연하게 받아들이는 것이다. 컴퓨터를 사용하면 우리는 동시에 여러개의 프로그램과 활동을 동시에 가동 한다. 예를 들어, 컴퓨터 전원을 켜고 마우스를 움직여 Chorme을 눌러 들어 오는 행위를 하는 동안 컴퓨터는 한가지 일만 하지 않는다. 마우스를 움직이고 크롬을 누를 때 크롬이 켜지면서 우리는 마우스를 움직일수도 있고 다른화면을 띄워 다른 작업을 할 수 있다. 크롬을 누르면 기다려야하고 마우스를 움직이면 마우스의 움직임만 가능하고 이런 식으로 컴퓨터가 동작한다면 그걸 사용하는 사람은 없을 것이다.

 

이렇게 예를 들어 봤는데, 이와 같이 여러가지 동작들을 동시에 처리하는 것을 멀티 태스킹이라하는데, 이는 여러개의 프로세스를 동시에 가동하는 것을 얘기한다. 이때 프로세서들은 각자 다른 메모리영역을 가지고 일을 수행 하기 때문에 부딪힐 일이 없이 서로 독립적으로 수행한다.

 

여기서 스레드의 개념이 등장한다.

 

스레드는 실제 프로세스내에서 작업을 수행 하는 주체를 의미한다. 모든 프로그램은 최소 한개 이상의 스레드가 모여 작업을 구성하며 2개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.


자바에서 스레드를 생성하는 방법에는 두가지가 있다.

 

1. Thread 클래스를 상속받기

2. Runnable 인터페이스를 구현해서 상속 받기

 

이렇게 두가지가 있는 데 직접 코드를 보면 더 쉽게 알 수 있다.


Thread 상속

class TimerThread extends Thread { // 스레드 코드, 객체생성해서 실행시킴
	int n = 0;

	@Override
	public void run() {
		while (true) {
			System.out.println("TimerThread running");
		}
	}
}

public class TestThread extends Thread {
	public static void main(String[] args) {
		TimerThread th = new TimerThread();
		th.start();
		while (true) {
			System.out.println("main Thread running");
		}
	}
}

 

 

우선 스레드를 상속 받아서 사용하는 방법이다.

 

TimerThread라는 클래스를 만들고 스레드를 extends한다.

상속받았을경우 run()메서드를 오버라이딩 해줘서 무한반복문안에 넣어준다.

 

그리고 메인메서드를 가지고 있는 TestThread에 스레드를 상속받아 

위의 클래스 TimerThread를 객체로 생성해서

start()라는 메서드를 가동시켜주면

 

TimerThread의 메서드 run과 TestThread의 main메서드에 있는 무한반복문이 동시에 각자 수행이 된다.

 

이렇게 동시에 출력이 되는 것을 볼 수 있는데, 이는 JVM이 둘중 아무거나 순서 같은거 없이 알아서 띄워주기 때문이다.

 

이런식으로 스레드의 멀티 태스킹에 대해 볼수 있다.

 

그리고 TimerThread클래스에 위와 같이 넣어주면 n이 0부터 1씩 증가하면서 출력 되는데 여기서 sleep메서드를 사용할 수 있다.

 

sleep은 사용을 멈췄다가 다시 가동하게 하는 역할인데 이를 millisecond초로 구분한다.

1밀리 세컨은 1000분의 1초 이므로 1000을 집어 넣으면 1초마다 꺼졌다 실행됐다 하는 것이다.

즉, 1초에 하나씩 숫자가 증가하면서 출력이 되는것을 볼 수 있다.

 

 

 

 


Runnable 인터페이스 구현

 

이것도 간단한데 Runnable 인터페이스 상속(implements사용)을 하면된다.

Runnable도 Thread상속과 마찬가지로 구현하고 싶은것을 run() 메서드에 넣으면 작동 된다.

 

Runnable 인터페이스는 몸체가 없는 메소드인 run() 메소드 단 하나만을 가지는 간단한 인터페이스이다. 그래서 보통은 Runnable인터페이스로 사용한다. 왜냐하면 간단하기도 하고, 다른 클래스를 이미 상속받은 상황에서 스레드를 같이 상속 받을 수는 없기 때문에 보통 간단하게 Runnable인터페이스로 상속 받는다.

 

위도 가동하면 스레드와 같은 결과가 반복되고 Runnable은 sleep이 없기 때문에 Thread를 import해서 sleep기능을 똑같이 사용할 수있다.


그럼 스레드를 사용하는 이유는 뭘까?

 

코드가 들어오는 족족 실행이 되는데 이를 하나씩 처리하면 엄청 비효율적이고 시간낭비이다.

이 때문에 스레드를 사용해서 여러가지일을 동시에 처리하는 것이다.

 

다만 너무 많은 일을 한번에 처리하다 보면 부하가 발생해 오류가 날수도 있고,

다른 사용자와 내가 서로 필요한 자원을 상대방이 가지고 있어서 무한대기 상태가 되는 교착상태(DeadLock)의 문제가 발생 할 수도 있으니 이에 주의해서 사용해야 한다.

 

교착상태는 다음 글에서 포스팅 해보겠다.