DEV/Java

[java] 멀티스레드(Multi thread)의 이해 / 구현

Bi3a 2023. 8. 1. 17:51

목차
반응형

멀티 스레드의 이해와 구현
java 기초 깨부수기

 

 

스레드(Thread) : 프로그램을 차례대로 실행하는 한 개의 흐름 단위

  • 싱글스레드 : 한 개의 프로세스 / 프로그램에 한개의 스레드만 사용하는 것
  • 멀티스레드 : 한 개의 프로세스 / 프로그램에 여러개의 스레드를 사용하는 것

      -> 장점 : 동일한 시간 흐름에 여러가지 명령 및 데이터 처리가 한꺼번에 일어나 동일한 업무를 처리속도가 향상된다.

 

 

멀티 스레드 장 / 단점

 

1) 장점 :

  • 응답 / 처리속도가 빨라진다. : 한개의 프로세스에서 처리해야 할 명령이 동시에 시작 및 종료된다. 
  • 자원 공유의 효율성이 증가한다. : 공유 메모리를 선언해 전역 변수인 static보다 비교적 적은 용량으로 값 공유 가능
  • 코드 가시성이 증가한다. : 스레드로 선언 시 스레드 간 어떤 데이터가 교환되는지 / 어떻게 병행하여 실행할지에 대한 코드가 간결해지고 명확해진다.

 

2) 단점 : 

  • ★데이터 무결성을 보장하기 위한 극강의 노력이 필요하다.
  • 코드 빌딩, 디버깅이 껄끄럽고 복잡시럽다. : 스레드가 동시에 실행되므로 어느 부분에 문제가 있는지 파악이 어렵다.
  • 동일한 데이터에 다중의 스레드가 동시에 접근 시 문제 : 병목현상, 값 변경시 데이터의 신뢰성 문제 등 문제 발생

ex ) A 스레드는 공유 메모리 Shared에서 i = 1라는 변수를 2로 변경 시 동시 접근한 B스레드는 1을 꺼낼 텐데..? 

-> 이는 java 데이터 동기화(Synchronized) 기능을 통해 해결 가능 / 별도로 Synchronized 포스트에서 다룰 예정

 

 

멀티 스레드의 구현

 

    1) 구현 방법 : 클래스 Thread를 상속 (Extends Thread) / 인터페이스 Runnable을 이식(implements Runnable)

  • 위의 두 방식은 모두 java5 시절 스레드를 구현하기 위한 옛날 API이어서 실 개발에는 잘 사용되지 않는다.
  • 요즘에는 Executor ~ Future, Flow까지 스레드를 구현하는 많은 방법이 있는데, 이는 점차 공부하면서 적어보겠다. 

 

    2) Extends Thread를 활용한 구현

  • 스레드가 처리할 내용은 상속받은 Thread 클래스 내 메소드인 run()을 오버라이드하여 재정의 및 구현해야한다.
  • run() 메서드를 구현 후 호출하기 위해서는 외부에서 스레드 생성 후 run() 메서드가 아닌 start()로 호출해야 한다.
  • Thread 클래스가 제공하는 기본 메서드 : run(현재 스레드의 작성된 코드 실행), join(다른 스레드의 작업 대기, 순서 제어), interrupt(다른 스레드가 끼어들게 함), slepp(현재 메서드를 일시 대기시킴)

 

예시 코드에서 한 개의 프로세스에서 두 개의 스레드가 동일한 시간흐름에서 처리됨을 확인하기 위해

첫 번째 스레드는 Sleep()과 println 메서드를 활용해 일정 시간마다 처리 동작을 측정하였다.

 

 

AThread 구현

java
닫기
class AThread extends Thread{ /* 10초 간 1초 단위로 실행 시간을 출력하는 쓰레드 */
public void run() {
try {
System.out.println("AThread : The World!");
for (int i = 1; i <= 10; i++) {
Thread.sleep(1000); /* Thread 클래스 안의 sleep 메소드 */
// 1000ms = 1초간 thread를 정지한다. * 사용에 try / catch 구문 필요
System.out.println("AThread : " + i + "초 경과");
}
System.out.println("[!] AThread 실행 완료");
}catch(Exception e){
System.out.println(e);
}
}
}

Thread를 Extends 한 AThread를 만들었다.

AThread.start()를 통해 스레드가 실행되는 시점부터 1초에 한 번씩 sleep()을 하며, for문 종료 시 최종 실행 시점부터 1000ms가 경과한 시점에 스레드 실행 완료라는 println을 출력할 것이다.

 

 

BThread 구현

java
닫기
class BThread extends Thread{ /* 0.5초 단위로 알파벳 출력하는 쓰레드 */
public void run() {
try {
System.out.println("BThread : 알파벳을 셀게요");
for (char alp = 'A'; alp <= 'Z'; alp++) {
Thread.sleep(500); /* Thread 클래스 안의 sleep 메소드 */
System.out.println("BThread : " + alp);
}
System.out.println("[!] BThread 실행 완료");
}catch(Exception e){
System.out.println(e);
}
}
}

Bthread는 500ms 단위로 sleep()을 하며 최종적으로 A부터 Z까지를 println으로 출력하는 스레드를 실행 후 종료된다.

 

 

main 구현

java
닫기
public class ThreadTest {
public static void main(String[] args) {
//실행할 쓰레드 객체 생성
AThread at = new AThread();
BThread bt = new BThread();
// 실행 순서 : at 쓰레드 -> main 메소드 1 -> bt 쓰레드 -> main 메소드 2
at.start();
System.out.println("MainMethod1 : 저는 빠르게 실행됩니다");
System.out.println("[!] MainMethod1 실행 완료");
bt.start();
int sum = 0;
for(int i = 0; i <= 100; i++) sum += i;
System.out.println("MainMethod2 : 1 ~ 100의 sum : "+sum);
System.out.println("[!] MainMethod2 실행 완료");
}
}

Athread와 Bthread 객체 생성 후 각자 start() 메서드를 통해 A, B 스레드 내부 run() 메소드를 작동시킨다.

추가적으로 메인 클래스 내부에도 별도 0 ~ 100까지의 합을 구하는 처리 메소드를 구현해 총 3개의 처리 순서를 확인했다.

(A스레드의 메서드, B스레드의 메소드, main 클래스의 처리)

 

 

실행 결과

java
닫기
MainMethod1 : 저는 빠르게 실행됩니다
[!] MainMethod1 실행 완료
AThread : The World!
BThread : 알파벳을 셀게요
MainMethod2 : 1 ~ 100의 sum : 5050
[!] MainMethod2 실행 완료
BThread : A
BThread : B
AThread : 1초 경과
BThread : C
AThread : 2초 경과
BThread : D
BThread : E
AThread : 3초 경과
BThread : F
BThread : G
AThread : 4초 경과
BThread : H
BThread : I
AThread : 5초 경과
BThread : J
BThread : K
AThread : 6초 경과
BThread : L
BThread : M
AThread : 7초 경과
BThread : N
BThread : O
AThread : 8초 경과
BThread : P
BThread : Q
AThread : 9초 경과
BThread : R
BThread : S
AThread : 10초 경과
[!] AThread 실행 완료
BThread : T
BThread : U
BThread : V
BThread : W
BThread : X
BThread : Y
BThread : Z
[!] BThread 실행 완료

실행 순서 : 코드에 나와있는 대로 A스레드 → Mainmethod1 → B스레드 → Mainmethod2이지만

비교적 처리가 빠른 main의 두 메서드가 가장 빠르게 처리되고, 이후 A, B스레드가 14000ms 내로 처리되었다.

이와 같이 동시다발적으로 처리가 진행됨을 확인할 수 있다.

 

예시에서 확인할 수 있듯이 

멀티 스레드 개념을 사용하면 일반적인 싱글 프로세스 - 싱글 스레드 보다

프로세스 처리하는 시간을 절감 / 메모리 공간을 효율적으로 활용할 수 있다.

 

 

멀티스레드를 활용한 공유 메모리 구현은 다음 포스팅에서 이어진다.

2023.08.17 - [DEV/Java] - [java] 멀티스레드를 활용한 공유 메모리 구현

 

[java] 멀티스레드를 활용한 공유 메모리 구현

0. 같이 보면 좋을 포스팅"공유 메모리와 멀티 스레드의 이해" 포스팅에서 이어집니다. [java 기초] 공유 메모리와 멀티 스레드의 이해0. 이전 포스팅 : "멀티 스레드의 이해와 구현" 에서 이어지는

doinitright.tistory.com

 

반응형