자바(Java) - 멀티 스레딩

Jan 08, 2024
자바(Java) - 멀티 스레딩

멀티 태스킹

멀티 태스킹이란?

💡
멀티 태스킹(multi-tasking)은 여러 개의 태스크(작업)를 동시에 실행하는 기법이다.
 
단일코어 CPU에서도 멀티태스킹은 가능하다.
운영체제가 CPU의 시간을 쪼개어서 작업들에 시간을 할당하기 때문에 작업들이 동시에 수행되는 것처럼 보인다.
물론 멀티코어를 가지고 있는 CPU라면 실제로도 동시에 실행될 것이다.
 
멀티 스레딩(multi-threading)은 병렬 작업의 아이디어를 하나의 애플리케이션 안으로 가져온 것이다.
 
 

프로세스와 스레드

💡
컴퓨터에는 프로세스(process)스레드(thread)라는 2가지의 실행 단위가 있다.
 
차이점은 프로세스는 자신만의 데이터를 가지는 데 반하여 스레드들은 모두 동일한 데이터를 공유한다.
 
 

멀티 스레딩을 사용하는 이유

응용 프로그램을 보다 빠르게 실행하기 위하여 멀티 스레딩을 사용한다.

스레드 생성과 실행

💡
자바에서 스레드를 생성하여 작업을 실행하는 2가지 방법이 있다.
 
  • Thread 클래스를 상속하는 방법
  • Runnable 인터페이스를 구현하는 방법
 
 

Thread 클래스를 상속하여 스레드 생성하기

  1. Thread를 상속받아서 클래스를 작성한다.
  1. run() 메소드를 재정의한다.
  1. Thread 객체를 생성한다.
  1. start()를 호출하여 스레드를 시작한다.
class MyThread extends Thread { // 1 public void run() { // 2 for(int i=0; i<=10; i++) { System.out.print(i + " "); } } } public class MyThreadTest { public static void main(String args[]) { Thread t = new MyThread(); // 3 t.start(); // 4 } }
 
 

Runnable 인터페이스를 구현하는 방법

  1. Runnable 인터페이스를 구현한 클래스를 작성한다.
  1. run() 메소드를 작성한다.
  1. Thread 객체를 생성하고 이때 MyRunnable 객체를 인수로 전달한다.
  1. start(0를 호출하여서 스레드를 시작한다.
class MyRunnable implements Runnable { // 1 public void run() { // 2 for(int i=0; i<=10; i++) { System.out.print(i + " "); } } } public class MyRunnableTest { public static void main(String args[]) { Thread t = new Thread(new MyRunnable()); // 3 t.start(); // 4 } }
 
 
💡
그렇다면 스레드를 생성하기 위해서는 어떤 방법을 사용하는 것이 좋은가?
 
Runnable 인터페이스를 사용하는 편이 더 일반적이다.
Runnable 객체는 Thread가 아닌 다른 클래스를 상속받을 수 있다.
 
 

람다식을 이용한 스레드 작성

앞에서 설명한 방법은 고전적인 방법이다. 즉, 람다식을 이용하지 않는 방법이다
람다식을 이용하면 얼마나 프로그램이 간단하게 작성되는지를 살펴보자.
0부터 10까지 카운트하는 프로그램을 람다식을 이용하여서 다시 작성하면 다음과 같다.
public class LambdaTest { public static void main(String args[]) { Runnable task = () -> { for (int i=0; i<=10; i++) { System.out.print(i + " "); } } new Thread(task).start(); } }

동기화

💡
스레드들은 동일한 데이터를 공유하기 때문에 매우 효율적으로 작업할 수 있다.
 
하지만 동일한 메모리를 사용하기 때문에 2가지의 문제가 발생할 수 있다.
 
하나는 스레드 간섭(thread interference)이고 또 하나는 메모리 불일치 오류(memory consistency error)이다.
 
  • 스레드 간섭(thread interference)
    • 스레드 간섭은 여러 개의 스레드가 공유된 데이터에 동시에 접근할 때 발생한다.
    •  
  • 메모리 불일치 문제(memory consistency error)
    • 메모리 불일치 오류는 스레드에 따라서 공유된 메모리의 값이 일치하지 않는 현상이다.
 
이러한 오류를 막는 도구를 동기화(synchronization)라고 한다.
 
 

동기화

  • 동기화 메소드(synchronized method)
    • //synchronized 키워드를 메소드 앞에 붙여주면 된다. class Printer { synchronized void print(int[] arr) { ... } }
    • 먼저 동기화된 메소드는 동시 호출되더라도 마이크로 단계들이 겹쳐지지 않는다.
      • 하나의 스레드가 동기화된 메소드를 실행하고 있으면, 그 스레드가 종료할 때까지 다른 모든 스레드는 중지된다.
        이것은 스레드 간섭 문제를 해결한다.
    • 동기화된 메소드가 종료되면 자동적으로 이후의 메소드 호출은 변경된 상태만을 볼 수 있다.
      • 이것은 메모리 불일치 오류 문제를 해결한다.
       
  • 동기화 블록(synchronized block)
    • class Printer { void print(int[] arr) throws Exception { synchronized(this) { for (int i=0; i<arr.length; i++) { System.out.print(arr[i] + " "); Thread.sleep(100); } } } }
    • 기화된 블록은 메소드의 특정 리소스에 대한 동기화를 수행하는 데 사용할 수 있다.
      • 메소드에 100줄의 코드가 있지만 10줄만 동기화하려는 경우, 동기화된 블록을 사용할 수 있다고 가정한다.
        동기화된 블록에 메소드의 모든 코드를 넣으면 동기화된 메소드와 동일하게 작동한다.
 
 
Share article

stwin755