[Java] Multithreading - Introduction to Multithreading

반응형

이번 글에서는 multi threading에 대해 알아보도록 하겠습니다.

1. Process & Thread

현대의 computer는 대부분 multiple core를 사용해 작업을 수행합니다. 따라서 multiple core의 최대치의 성능을 발휘하기 위해선 program 을 작성할때 이를 고려해 작성해야 합니다.

1-1) one process & one thread

일반적으로 하나의 program은 1개의 process와 1개의 thread를 사용합니다.

task manager의 process 탭을 살펴보면 위와 같이 실행되고 있는 program 별로 process가 생성되어 있는 것을 확인할 수 있습니다.

performance 탭에서는 현재 실행되고 있는 process & thread의 갯수를 확인할 수 있습니다.

1-2) multiple processes

하나의 program은 multiple process로 실행될 수 있습니다.

대표적인 예로 google의 chrome을 예로 들 수 있습니다.

chrome을 실행한뒤 task manager를 확인해보면 위와 같이 multiple process가 실행되고 있는 것을 확인할 수 있습니다.

이는 chrome이 많은 양의 memory를 사용하는 이유입니다. multiple process로 chrome을 실행하므로써, 여러개의 탭을 실행하는 경우에 하나의 탭에 crash가 발생하더라도 독립적인 process로 실행한 다른 탭들은 영향을 받지 않습니다.

1-3) multiple threads

하나의 process는 multiple threads로 실행 될 수 있습니다.

이때 default로 생성되는 single thread를 'Main Thread'라고 부릅니다.

2. MultiThreading

multiple threads는 왜 사용되어야 할까요? 😅

예를 들어 아래와 같이 간단한 Java GUI 프로그램이 있다고 가정해보겠습니다.

이 프로그램은 0에서부터 input 값으로 입력받은 long 값까지 더해 alert 창을 통해 결과값을 보여줍니다. 또한, 이러한 연산을 수행하기위해 별도의 thread 없이 main thread만을 사용합니다.

이때 5000을 입력한 경우 아래와 같이 25000000을 alert 합니다.

이때 만약 input에 굉장히 큰 숫자를 입력한 경우 연산을 수행해 결과값을 return하기 전까지 program은 stuck 상태 즉, 멈춰버리게 됩니다.

따라서 이를 방지하기위해선, 연산을 수행하는 thread를 따로 생성해 연산을 수행하게 하고 반환된 결과값을 response 하는 방법으로 개선해야 합니다.

** "You should use multithreading when you want to perform heavy operations without "blocking" the flow"** (stackoverflow)

3. Multithreading Synchronization

만약 mutli threading 을 사용할 경우 아래와 같은 문제를 방지해 thread-safe 하도록 프로그램을 작성해야 합니다.

3-1) problem

아래와 같이 간단한 Counter Class가 있다고 가정해보겠습니다. 이 클래스는 increment() 메서드를 통해 count value의 값을 증가시킵니다.

class Counter {
    int count;

    public void increment() {
           count++:
    }
}

위의 Counter 클래스를 사용하는 Main 함수가 아래와 같이 작성되었을때 최종적으로 얻게되는 Count의 값은 무엇일까요? 🙂

public static void main(String[] args) {
    Counter c = new Counter();

    Thread t1 = new Thread(new Runnable(){
        public void run(){
            for(int i=0; i<=1000; i++){
                c.increment();
            }
        }
    });

    t1.start();

    Thread t2 = new Thread(new Runnable(){
        public void run(){
            for(int i=0; i<=1000; i++){
                c.increment();
            }
        }
    });

    t2.start();

    // wait until t1 & t2 thread is done
    t1.join();
    t2.join();

    // print result
    System.out.println("Result : " + c.count);
}

두개의 thread가 1000씩 2번 increment() 함수를 호출했으므로, 2000이 출력될까요??..😅

그렇지 않습니다. 위의 메서드를 수행해보면.. 절대로 2000이 return 되지 않습니다. 또한 매 실행시마다 결과값이 달라지는 것을 확인할 수 있습니다.

이유는 Counter Class의 increment 함수가 thread-safe하게 작성되지 않았기 때문입니다.

increment 함수의 count++는 풀어보면 'count = count + 1'를 의미합니다.

즉, count 변수에 1을 더하기전에 count 값을 먼저 fetch 하는 과정을 거치게 되는데 t1과 t2가 동시에 increment 함수에 접근하는 경우에 이 fetch 하는 과정에서 문제가 발생합니다.

예를 들어 현재 count 값이 600인 경우 t1도 count를 600으로 fetch하고, t2도 count를 600으로 fetch한 경우 t1과 t2 모두 count를 601로 update하게 됩니다.

원래대로라면 순서대로 위의 작업을 처리해 602가 되었어야 의도한대로 작동한 것입니다.

3-2) solve

위와 같은 문제를 해결하기 위해선 synchronized 변수를 사용해 동일한 메서드에 동시에 오직 하나의 thread만 접근할 수 있도록 제한해야 합니다.

class Counter {
    int count;

    // only one thread can access
    public synchronized void increment() {
           count++:
    }
}

위와 같이 synchronized를 선언한경우, increment 메서드에 접근중인 thread가 있을 경우 접근을 시도한 thread는 해당 thread 가 free 해질때까지 기다렸다가 작업을 수행하게 됩니다.

그 결과 우리가 원했던 2000이라는 결과값을 획득 할 수 있게 됩니다. 👏👏👏

MultiThreading Synchronization에 대한 자세한 내용은 다음글에서 자세히 다뤄보도록 하겠습니다. 😎


추천서적

 

이것이 자바다:신용권의 Java 프로그래밍 정복

COUPANG

www.coupang.com

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음


반응형

댓글

Designed by JB FACTORY