스레드는 하나의 프로세스 내에서 실행되는 흐름의 단위로, 멀티스레드는 하나의 프로세스 내에서 여러 스레드를 동시에 실행시키는 방법이다.
자바의 스레드에 대한 자세한 내용은 지난 시간에 정리한 글을 참고하시기 바란다.
이번에는 직접 스레드를 생성하고 실행하는 방법에 대해 알아보자.
멀티 스레드는 동시성 방식이나 병렬성 방식으로 수행된다. 각 단어의 의미를 이해해보자.
동시성과 병렬성은 다음 그림으로 설명할 수 있다. 이 그림에서 동시성과 병렬성 단어 차이는 코어 수와 관련이 있구나를 생각해 볼 수 있다.
그럼 코어는 무엇일까?
컴퓨터의 시스템에는 가장 핵심적인 역할을 하는 CPU(Central Processing Unit), 즉 중앙 처리 장치가 있다. 이는 컴퓨터 내부 프로그램의 명령어를 해석하고 연산처리를 하여 외부에 출력하는 역할을 한다. 또한 컴퓨터의 전반적인 시스템을 제어하기 때문에 사람의 두뇌의 역할을 한다고도 한다.
여기서 코어는 CPU의 핵심적인 역할을 수행하는 중심부를 말한다. 코어에서 시스템의 모든 연산을 처리한다. 즉 코어가 많으면 컴퓨터의 성능을 좌우한다고 이야기한다.
그럼 프로세스 내에서 실행되는 흐름 단위인 스레드와 비슷한 개념이 아닌가하는 생각을 할 수 있다. 앞에서 이야기한 코어는 하드웨어 관점에서 작업 수행자를 이야기하고, 스레드는 소프트웨어 관점에서 논리적인 작업 처리 단위를 말한다.
그럼 다시 두 단어 차이를 이해해보자.
동시성은 싱글 코어에서 멀티스레드를 동작시킨다. 하나의 작업 수행자가 멀티 태스킹을 하기 때문에 여러 스레드가 번갈아가면서 실행된다. 이것은 하나의 일이 한번에 일어난다라는 말이 아니다.
이때 다른 작업으로 바꿔서 실행하는 것을 콘텍스트 스위칭(Context Switching)이 일어났다고 한다.
병렬성은 멀티 코어에서 멀티스레드를 동작시킨다. 하나 이상의 스레드를 포함하는 각 코어들이 동시에, 즉 한번에 실행되는 성질이다.
이 특성은 각 코어들이 동시에 실행되므로 CPU의 유휴시간(사용되지 않는 시간)이 줄어들어 성능이 좋지만, CPU보다 처리해야하는 프로세스나 스레드수가 많을 경우 작업 시작 전 대기 상태가 발생한다. 따라서 유휴시간을 줄이고 성능을 높일 수 있는 적당한 스레드 개수를 아는 것이 필요하다.
Main 스레드가 생성한 스레드들은 사용자 스레드이다. 일단 데몬은 사용자가 직접적으로 제어하지 않고, 백그라운드를 돌면서 작업을 하는 프로그램을 말한다.
이처럼 데몬 스레드는 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드이다. 주 스레드의 보조 역할이므로 주 스레드가 종료되면 데몬 스레드의 의미가 없어져 주 스레드가 종료되면 강제적으로 자동 종료된다. 따라서 데몬 스레드는 무한 루프에 빠질 위험이 없다.
이러한 특징을 가진 데몬스레드는 주로 가비지 컬렉터, 자동 저장, 화면 자동갱신을 수행하는데 사용된다.
데몬 스레드를 만드는 방법은 메인 스레드가 데몬이 될 스레드의 setDaemon(true)
를 호출해 지정한다.
IllegalThreadStateException
이 발생하기 때문에 start() 메서드 호출전에 데몬을 지정해주어야 한다.예시를 통해 이해해보겠다.
ThreadDaemonExample.java - 메인 스레드
public class ThreadDaemonExample {
public static void main(String[] args) {
AutoSaveThread autoSaveThread = new AutoSaveThread();
autoSaveThread.setDaemon(true); // AutoSaveThread를 데몬스레드로 지정한다.
autoSaveThread.start(); // thread 시작
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println("메인 스레드 종료");
}
}
AutoSaveThread.java - 1초 주기화 save() 메서드를 호출하는 데몬 스레드
package etc;
public class AutoSaveThread extends Thread{
public void save() {
System.out.println("저장");
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
break;
}
save();
}
}
}
실행결과
저장
저장
저장
저장
메인스레드 종료
1초부터 4초까지 1초 주기로 데몬스레드의 save() 메서드가 실행디고, 메인 메서드에서 5초후에 종료 문구가 출력된 것을 확인할 수 있었다.
여러 스레드를 실행할 때 작업의 중요도에 따라 스레드의 우선 순위를 다르게 설정할 수 있다. 이처럼 멀티스레드의 순서를 정하는 것을 스레드 스케쥴링(thread scheduling)이라고 한다.
스레드 스케줄링 방식은 우선순위(priority)방식과 순환 할당(Round Robin) 방식이 있다.
순환 할당방식은 JVM에 의해 결정되기 때문에 임으로 수정할 수 없다. 따라서 우선순위를 부여하고 싶다면 setPriority() 메서드로 우선순위를 부여한다.
우선 순위에 대한 특징은 다음과 같다.
Thread.NORM_PRIORITY
: 5Thread.MIN_PRIORITY
: 1Thread.MAX_PRIORITY
: 10public class ThreadExample extends Thread{
public static void main(String[] args) {
Thread.currentThread().setPriority(1); // 우선순위 1로 설정
System.out.println(Thread.currentThread().getPriority());
ThreadExample threadExample = new ThreadExample();
threadExample.start();
threadExample.setPriority(10); // 우선순위 10으로 설정
}
@Override
public void run() {
System.out.println(this.getPriority());
}
}
Reference