ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] #7 스레드(Thread)
    Software engineer/Java 2023. 5. 2. 22:52

    스레드(Thread)

    Java에서 스레드(Thread)는 프로그램의 실행 흐름을 분기하여 동시에 처리할 수 있도록 하는 기능입니다. Java에서 스레드를 사용하면 여러 작업을 동시에 실행할 수 있으므로, 병렬성(parallelism)을 구현할 수 있습니다. Java에서 스레드를 사용하기 위해서는 다음과 같은 방법들이 있습니다.

     

    Thread 클래스 상속

    • Thread 클래스를 상속받아 run() 메소드를 오버라이딩하여 스레드 동작을 구현합니다.
    class MyThread extends Thread {
        @Override
        public void run() {
            // 스레드 동작 구현
        }
    }
    
    MyThread thread = new MyThread();
    thread.start();

     

     

    Runnable 인터페이스 구현

    • Runnable 인터페이스를 구현하여 run() 메소드를 구현하고, Thread 클래스의 생성자로 Runnable 객체를 전달하여 스레드 동작을 구현합니다.
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 스레드 동작 구현
        }
    }
    
    MyRunnable runnable = new MyRunnable();
    Thread thread = new Thread(runnable);
    thread.start();

     

     

    병렬성과 동시성

    병렬성(Parallelism)과 동시성(Concurrency)은 비슷한 개념으로 보일 수 있지만, 미묘한 차이가 있습니다.

     

    두명이 두개의 일을 각각 처리하고 있다.

    병렬성(Parallelism)

    • 병렬성은 여러 개의 작업을 동시에 처리하는 것을 의미합니다. 이는 작업들을 분할하여 각각의 작업을 병렬로 처리함으로써 전체 작업을 빠르게 처리할 수 있게 합니다.
    • 병렬성을 구현하기 위해서는 여러 개의 실행 컨텍스트(execution context)를 가지고 작업들을 병렬로 처리해야 합니다.

     

    한명이 두가지 일을 동시에 처리하고 있다.

    동시성(Concurrency)

    • 동시성은 여러 개의 작업을 동시에 처리하는 것처럼 보이지만, 실제로는 하나의 작업만 실행되고 있으며 작업들이 번갈아가면서 실행됩니다. 이는 작업을 동시에 처리하지 않고, 각각의 작업을 작은 단위로 쪼개서 번갈아가며 실행함으로써 전체 작업을 빠르게 처리할 수 있게 합니다.
    • 동시성을 구현하기 위해서는 실행 컨텍스트(execution context)를 이용하여 작업들을 번갈아가며 처리해야 합니다.

     

    즉, 병렬성은 여러 개의 작업을 실제로 동시에 처리하며, 동시성은 여러 개의 작업을 하나의 실행 컨텍스트에서 번갈아가면서 처리합니다. 이러한 차이로 인해, 병렬성은 보통 여러 개의 프로세서 또는 코어를 사용하여 처리하는데 반해, 동시성은 보통 단일 프로세서 또는 코어에서 처리됩니다.

     

     

    Java에서는 멀티 스레드 프로그래밍을 통해 동시성을 구현할 수 있으며, 병렬성을 구현하기 위해서는 Parallel Stream, Executor Service 등을 이용하여 구현할 수 있습니다.

     

     

    하지만, 하나의 공유자원을 여러 스레드에서 동시에 접근하여 사용하게 되면 때때로 우리가 예상치 못한 결과가 나타나게 됩니다. 여기에서는 이러한 문제를 해결하기 위해 사용되는 스레드 동기화 (Thread Synchronized)에 대해서 알아보도록 하겠습니다. 

     

     

    동기화(Synchronized)

    Thread에서 synchronized는 스레드 간 공유하는 자원에 대한 접근을 제어하는데 사용됩니다. 여러 스레드가 동시에 공유 자원에 접근하는 경우, 값의 불일치나 예기치 않은 결과가 발생할 수 있습니다. 이를 방지하기 위해 synchronized를 이용하여 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 제어합니다.

     

    다음은 Counter 클래스에서 count 값을 증가시키는 increment() 메소드를 synchronized로 선언한 예제입니다. 

    public class Counter {
        private int count = 0;
    
        public synchronized void increment() {
            count++;
        }
    
        public int getCount() {
            return count;
        }
    }

    위 코드에서 increment() 메소드는 synchronized로 선언되어 있습니다. 따라서, increment() 메소드에 접근하는 스레드는 한 번에 하나씩만 실행됩니다. getCount() 메소드는 동기화 처리가 필요하지 않으므로 synchronized를 사용하지 않았습니다.

     

     

    다음은 Counter 객체를 생성하여 병렬로 실행되는 여러 개의 스레드에서 increment() 메소드를 호출하는 예제입니다.

    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Counter counter = new Counter();
    
            // 10개의 스레드 생성
            Thread[] threads = new Thread[10];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for (int j = 0; j < 1000; j++) {
                        counter.increment();
                    }
                });
            }
    
            // 스레드 실행
            for (Thread thread : threads) {
                thread.start();
            }
    
            // 모든 스레드의 실행이 끝날 때까지 대기
            for (Thread thread : threads) {
                thread.join();
            }
    
            System.out.println("Count: " + counter.getCount());
        }
    }

    위 코드에서 Counter 객체를 생성한 후, 10개의 스레드를 생성합니다. 각 스레드에서는 1000번씩 increment() 메소드를 호출하여 count 값을 증가시킵니다. 마지막으로, 모든 스레드의 실행이 끝난 후 getCount() 메소드를 호출하여 count 값을 출력합니다.

    synchronized를 이용하여 increment() 메소드에 접근하는 스레드를 하나씩 순차적으로 실행시키므로, count 값은 예상대로 10000이 출력됩니다.

    만약 Counter 클래스 increment() 메소드의 synchronized를 선언하지 않는다면 10000이라는 결과를 얻을 수 없습니다.

     

     

    스레드의 장단점

     

    장점:

    • 멀티태스킹: 스레드를 이용하면 여러 작업을 동시에 처리할 수 있습니다. 이를 통해 시스템의 응답성과 처리량을 높일 수 있습니다.
    • 비동기 처리: 스레드를 이용하면 비동기 처리를 구현할 수 있습니다. 예를 들어, 네트워크 통신이나 파일 입출력 작업 등은 블로킹(blocking) 작업이므로, 스레드를 이용하여 비동기적으로 처리하면 응용프로그램의 반응성을 향상시킬 수 있습니다.
    • 분산 처리: 스레드를 이용하여 작업을 분산 처리하면 처리속도를 높일 수 있습니다.

     

    단점:

    • 동기화 문제: 스레드를 이용할 때, 스레드 간 공유 자원에 대한 접근을 제어하기 위한 동기화 문제가 발생합니다. 이를 해결하기 위해서는 적절한 동기화 기법을 사용해야 하며, 동기화 처리가 잘못될 경우, 성능 저하나 데드락(deadlock) 등의 문제가 발생할 수 있습니다.
    • 복잡성: 스레드를 이용하면 프로그램의 복잡도가 증가합니다. 스레드 간의 통신과 동기화 처리 등에 대한 고려가 필요합니다.
    • 자원 소모: 스레드는 별도의 스택과 레지스터를 필요로 하므로, 메모리와 CPU 자원을 소모합니다. 따라서, 스레드를 적절하게 사용하지 않으면 시스템 성능에 영향을 줄 수 있습니다.

     

    스레드는 여러 작업을 동시에 처리할 수 있는 장점이 있지만, 동기화 문제, 복잡성, 자원 소모 등의 단점도 함께 존재합니다. 따라서, 스레드를 적절하게 사용하여 성능을 향상시키는 것이 중요합니다.

     

     

Designed by Tistory.