- 자바는 기본적으로 멀티 스레드 기능을 제공합니다.
자바의 멀티 스레드를 활용하면, 여러 요청을 동시에 처리할 수 있다는 장점이 있습니다.
그런데 우선은 자바의 멀티 스레드를 이해하기 위해서는 자바의 스레드를 이해해야 합니다.
- 이번 글에서는 '스레드란?', 'Runnable 인터페이스와 Thread 클래스', 'Sleep() 메소드', 'synchronized'에 대해 다뤄보겠습니다.
1) 스레드란?
- JVM이 시작되면 자바 프로세스가 시작되는데, 하나의 자바 프로세스에는 여러 개의 스레드가 존재합니다.
여기서 스레드란 CPU에 작업 요청을 하는 실행 단위를 의미합니다.
스레드는 프로세스에 비해 2가지 장점을 갖고 있습니다.
첫번째로, 스레드는 프로세스에 비해 메모리 용량을 적게 차지합니다. 예를 들어, JVM 프로세스 하나를
실행하면 32~64MB 정도의 물리 메모리를 점유하는데, 스레드는 1MB 이내의 메모리를 점유합니다.
따라서 스레드를 다른 말로 경량 프로세스(LightWeight Process)라고 합니다.
두번째로, 스레드는 프로세스에 비해 컨텍스트 스위칭 오버헤드가 적습니다.
그 이유는 스레드 컨텍스트 스위칭은 프로세스 컨텍스트 스위칭과 다르게
메모리 주소 공간에 대한 스위칭이 필요 없기 때문입니다.
2) Runnable 인터페이스와 Thread 클래스
- 자바에서 스레드를 생성하는 방법에는 크게 2가지가 있습니다.
첫째는 Runnable 인터페이스, 둘째는 Thread 클래스입니다.
우선 Runnable 인터페이스를 생성하는 예제 코드는 다음과 같습니다.
package e.thread;
public class RunnableSample implements Runnable{
public void run(){
System.out.println("This is RunnableSample's run() method.");
}
}
다음은 Thread 클래스를 생성하는 예제 코드입니다.
package e.thread;
public class ThreadSample extends Thread{
public void run(){
System.out.println("This is ThreadSample's run() method.");
}
}
위의 두 개의 스레드 클래스를 실행하는 RunThreads 클래스를 다음과 같이 만들 수 있습니다.
package e.thread;
public class RunThreads{
public static void main(String[] args){
Runthreads threads = new RunThreads();
threads.runBasic();
}
public void runBasic(){
RunnableSample runnable = new RunnableSample();
new Thread(runnable).start();
ThreadSample thread = new ThreadSample();
thread.start();
System.out.println("RunThreads.runBasic() method is ended.");
}
}
- thread가 수행되는 메소드는 run() 이고,
thread를 시작하는 메소드는 start() 입니다.
- 여기서 중요한 것은 runBasic() 메소드의 실행 결과를 살펴보는 것입니다.
runBasic() 메소드는 다음과 같은 결과가 나옵니다.
This is RunnableSample's run method.
This is ThreadSample's run method.
RunThreads.runBasic() method is ended.
- 하지만 항상 위와 같이 나오는 것은 아닙니다.
이렇게 결과가 나올 수도 있습니다.
This is RunnableSample's run method.
RunThreads.runBasic() method is ended.
This is ThreadSample's run method.
- 위와 같은 결과가 나오는 이유는 순차적(sequential) 프로그래밍과는 달리,
자바의 스레드는 하나의 스레드가 실행되면, 그 스레드가 끝나기 전에, 다른 스레드가 실행될 수 있기 때문입니다.
이 때, 먼저 실행된 스레드가 먼저 끝날 수도 있고, 나중에 실행된 스레드가 먼저 끝날 수도 있습니다.
이렇게 자바에서 여러 개의 스레드가 동시에 실행 가능한 것을 '멀티 스레드'라고 합니다.
3) Sleep() 메소드
- Thread 클래스에는 여러 메소드가 있습니다. 그 중에서 많이 사용되는 메소드 중 하나가 sleep() 메소드입니다.
Sleep() 메소드는 매개변수로 넘어온 시간(1/1000초)만큼 대기하는 기능을 합니다.
Sleep() 메소드의 예시 코드는 다음과 같습니다.
package e.thread;
public class EndlessThread extends Thread{
public void run(){
while(true){
try{
System.out.println(System.currentTimeMillis());
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
- Sleep() 메소드를 사용할 때 주의할 점은 반드시 try-catch문과 묶어서, 적어도 InterruptedException과 같이 사용해야 한다는 점입니다. 여기서 '적어도'라는 의미는 InterruptedException의 상위 클래스인 Exception 클래스를 사용해도 된다는 의미입니다.
※ InterruptedException이란?
- InterruptedException은 스레드가 대기중이거나 잠자는중(sleeping)이거나 점유중일 때,
방해를 받는(Interrupted)다면 발생합니다.
4) synchronized
- Synchronized는 자바의 예약어로, 특정 메소드나 블록을 '스레드 안전(thread safe)'하게 만들어주는 역할을 합니다.
예를 들어, 다음과 같은 plus() 메소드가 있다고 할 때,
public void plus(int value){
amount += value;
}
- 이 메소드를 synchronized로 선언하면 다음과 같습니다.
public synchronized void plus(int value){
amount += value;
}
- 이 synchronized라는 키워드가 메소드에 추가되면, 10개의 스레드가 접근하든, 100개의 스레드가 접근하든,
한 번에 하나의 스레드만 이 메소드를 수행하게 됩니다.
즉, 하나의 스레드가 해당 메소드의 사용을 종료할 떄까지, 다른 스레드는 기다립니다.
- 단, synchronized를 메소드에 추가했을 때, 발생하는 문제점도 있습니다.
예를 들어, 어떤 클래스에 30줄짜리 메소드가 있다고 가정해봅니다.
그리고 그 메소드에는 amount라는 인스턴스 변수가 있고,
30줄짜리 메소드 중 amount라는 변수는 한 줄에서만 다룬다고 해봅시다.
이 때, 메소드에 synchronized를 처리하면, amount가 아닌 다른 29줄을 처리할 때까지
대기 시간이 발생하므로 비효율적입니다.
따라서 이러한 경우는 amount라는 변수를 처리하는 부분에만 synchronized 처리를 하면 됩니다.
public void plus(int value){
synchronized(this){
amount += value;
}
}
pubhlic void minus(int value){
synchronized(this){
amount -= value;
}
}
- 자바 String 클래스에 대해서 배운 적이 있다면, StringBuffer 클래스는 스레드 안전하고, StringBuilder 클래스는 스레드 안전하지 않다는 것에 대해 알고 있을 것입니다. 이러한 차이가 발생하는 이유도 StringBuffer 클래스는 synchronized 블록으로 주요 데이터 처리 부분을 감싸 두었고, StringBuilder 클래스는 synchronized를 사용하지 않았기 때문입니다.
참고
자바의 신 2권
'Java' 카테고리의 다른 글
자바에서 소스 코드가 실행되는 과정 (0) | 2022.08.10 |
---|---|
HashMap vs HashTable vs ConcurrentHashMap (0) | 2022.08.06 |
CheckedException vs UncheckedException (0) | 2022.08.03 |
자바 float과 double 자료형 (0) | 2022.07.31 |
자바 int와 long 자료형 (0) | 2022.07.31 |