CS

[CS] 동시성과 병렬성의 개념

kahnco 2025. 4. 15. 11:55
반응형

이 글에서는 멀티스레드 프로그래밍의 핵심 개념인 동시성(Concurrency)병렬성(Parallelism) 을 구체적으로 설명하고, Java Spring 프레임워크에서 이를 어떻게 확인하고 활용할 수 있는지 예제 코드와 함께 살펴본다.


📌 1. 동시성(Concurrency)이란?

동시성은 여러 작업이 논리적으로 동시에 실행되는 것처럼 보이도록 처리하는 방식을 의미한다.

▶︎ 동시성의 핵심 개념

  • 단일 CPU에서도 구현 가능
  • 여러 작업을 번갈아 실행하면서, 사용자는 작업이 동시에 이루어지고 있다고 느낀다.
  • 컨텍스트 스위칭(Context Switching)을 활용하여 작업 간 전환이 빠르게 이루어진다.

▶︎ 동시성 예시

  • 웹서버가 여러 클라이언트 요청을 동시에 처리하는 경우
  • Java에서 여러 스레드(Thread)를 만들어 논리적인 병행 처리를 수행하는 경우

▶︎ 동시성의 장점

  • 자원 활용을 극대화하여 작업의 응답성을 높임
  • 대기 시간이 긴 I/O 작업을 수행할 때 효율적으로 활용 가능

📌 2. 병렬성(Parallelism)이란?

병렬성은 여러 작업이 물리적으로 동시에 수행되는 방식을 의미한다.

▶︎ 병렬성의 핵심 개념

  • 반드시 다중 CPU 또는 다중 코어 환경이 필요하다.
  • 각 작업이 서로 독립된 CPU 또는 코어에서 동시에 처리된다.
  • 여러 작업을 동시에 처리하여 총 작업 시간을 줄인다.

▶︎ 병렬성 예시

  • 멀티코어 환경에서의 데이터 처리 및 연산 작업
  • Java의 병렬 스트림(Parallel Stream)을 이용하여 데이터를 병렬 처리하는 경우

▶︎ 병렬성의 장점

  • 작업 속도가 빨라지고 처리량이 증가한다.
  • CPU 집약적인 작업에서 성능 향상 효과가 크다.

📌 3. 동시성과 병렬성의 차이점 비교

구분 동시성 (Concurrency) 병렬성 (Parallelism)

정의 논리적으로 여러 작업이 동시에 실행 물리적으로 여러 작업이 동시에 실행
CPU 조건 단일 CPU에서도 가능 멀티 코어 CPU 필수
목적 응답성 향상, 자원 활용 최적화 처리 성능 향상, 처리 속도 증가
예시 웹서버의 멀티스레드 요청 처리 데이터 분석, 병렬 연산 처리

📌 4. Java Spring에서 동시성과 병렬성의 구현 예제

Spring에서는 비동기(@Async) 기능을 통해 손쉽게 동시성과 병렬성을 구현할 수 있다.
예제를 통해 실제로 이를 확인해보자.

✅ Spring 프로젝트에서 비동기 설정

@Async를 사용하기 위한 설정을 먼저 수행한다.

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(8);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("Async-Thread-");
        executor.initialize();
        return executor;
    }
}

✅ 동시성(Concurrency) 예제

다수의 작업이 논리적으로 동시에 수행되는 모습을 확인한다.

비동기 서비스 클래스 작성:

@Service
public class ConcurrentService {

    @Async("threadPoolTaskExecutor")
    public void processTask(int taskNumber) throws InterruptedException {
        System.out.println("작업 시작: " + taskNumber + " - 스레드: " + Thread.currentThread().getName());
        Thread.sleep(3000); // 3초 지연
        System.out.println("작업 완료: " + taskNumber + " - 스레드: " + Thread.currentThread().getName());
    }
}

컨트롤러 작성 (호출부):

@RestController
public class ConcurrentController {
    
    @Autowired
    private ConcurrentService concurrentService;

    @GetMapping("/concurrent")
    public String executeConcurrentTasks() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            concurrentService.processTask(i);
        }
        return "Concurrent Tasks Started";
    }
}

실행 결과:
스레드가 교차하면서 여러 작업이 논리적으로 동시에 처리되는 것을 로그를 통해 확인할 수 있다.


✅ 병렬성(Parallelism) 예제 (병렬 스트림 사용)

실제 CPU 코어를 활용하여 병렬 처리가 이루어지는 모습을 확인한다.

서비스 클래스 작성:

@Service
public class ParallelService {

    public int sumUsingParallelStream() {
        return IntStream.rangeClosed(1, 1_000_000)
                        .parallel() // 병렬 스트림 사용
                        .sum();
    }
}

컨트롤러 작성 (호출부):

@RestController
public class ParallelController {

    @Autowired
    private ParallelService parallelService;

    @GetMapping("/parallel")
    public String executeParallelTasks() {
        long start = System.currentTimeMillis();

        int sum = parallelService.sumUsingParallelStream();

        long end = System.currentTimeMillis();

        return "합계: " + sum + ", 소요 시간: " + (end - start) + "ms";
    }
}

실행 결과:
멀티코어 CPU에서 작업이 동시에 진행되어, 단일 스트림 대비 빠르게 결과를 도출하는 것을 볼 수 있다.


📌 5. 사용 시 주의점 및 팁

  • 동시성을 구현할 때 스레드의 개수가 너무 많으면 컨텍스트 스위칭 비용이 증가하여 오히려 성능이 떨어질 수 있으므로, 적절한 스레드 풀 크기를 설정해야 한다.
  • 병렬 스트림은 데이터의 크기가 작거나 단순한 작업에서는 오히려 성능 저하를 유발할 수 있으므로, 충분히 복잡하거나 양이 많은 작업에서 사용하는 것이 적합하다.

📌 6. 마무리 및 요약

이 글에서는 다음의 내용을 다루었다.

  • 동시성은 논리적으로 동시에 처리하는 방식으로, 응답성과 자원 활용을 높인다.
  • 병렬성은 물리적으로 동시에 처리하는 방식으로, 처리 성능과 속도를 증가시킨다.
  • Spring에서 @Async와 병렬 스트림을 통해 두 가지 개념을 간편하게 구현 가능하다.
반응형