동시성 (Concurrency)
- Task들이 빠르게 전환되면서 실행되어 동시에 실행되는 것처럼 보이는 것
- 싱글 코어(cpu 내에서 실제로 연산 및 명 실행을 담당하는 장치)에서는 여러 작업이 동시적으로 실행되며 Task 간 Context Switch가 발생하면서 여러 작업들을 동시에 실행하는 것처럼 보입니다.
- Context Switch: CPU/코어에서 실행 중이던 프로세스/스레드가 다른 프로세스/스레드로 교체되는 현상
- 유휴 시간(idle time)을 최소화하여 CPU 활용도를 높이는 것이 목표입니다. (유후 시간: 컴퓨터가 작동 가능한대도 작업을 하지 않는 시간)
- 두 개 이상의 작업이 싱글 코어 or 다중 코어 CPU의 동일한 코어에서 실행되는 경우, 동시에 동일한 리소스에 접근할 수 있습니다.
- 데이터 읽기 작업은 병렬로 수행되고 안전하지만, 쓰기 액세스 시에는 프로그래머가 데이터 무결성을 유지해야 합니다.
병렬성(Parallellism)
- 프로그램의 독립적인 작업을 동시에 실행하는 능력
- 동시 작업과는 달리, 이러한 작업은 다르 프로세서, 코어, 다른 프로세서, 또는 분산 시스템일 수 있는 완전히 다른 컴퓨터에서 동시에 실행될 수 있습니다.
- 실제 애플리케이션의 컴퓨팅 속도에 대한 요구사항이 증가함에 따라 병렬 처리는 보편화되고 있습니다.
Example: 분산 시스템
- 여러 컴퓨터 시스템으로 구성되지만, 단일 시스템으로 실행됩니다.
- 시스템에 속한 컴퓨터들은 물리적으로 가까이에 위치하여 로컬 네트워크로 연결될 수도 있으며, 멀리 떨어져 있어 광역 네트워크(WAN)로 연결될 수 있습니다.
Concurrency, Parellelism 비교 정리
동시성 | 병렬성 |
동시에 실행되는 것 같이 보이는 것 | 실제로 동시에 여러 작업이 처리되는 것 |
싱글 코어에서 멀티 쓰레드(Multi thread)를 동작 시키는 방식 | 멀티 코어에서 멀티 쓰레드(Multi thread)를 동작시키는 방식 |
한번에 많은 것을 처리 | 한번에 많은 일을 처리 |
논리적인 개념 | 물리적인 개념 |
Concurrency vs Parellelism
- 2개의 코어 있다고 가정합니다.
- 동시성의 경우에는 Core-1, 2에서 시간의 흐름에 따라 Task-1, 2를 번갈아가면서 실행합니다.
- ex) 텍스트 편집기 (파일을 저장 및 인쇄할 때 텍스트를 입력할 수도 있습니다.)
- 병렬성의 경우는 각 코어에서 Task를 독립적으로 실행합니다.
- ex) Hadoop 기반의 분산 데이터 처리 (클러스터에서 대 규모 데이터 처리를 수반)
- 동시성과 병렬성은 복잡한 개념인 만큼, 깊은 개발 지식을 요구합니다. 동시성 환경을 잘 고려하지 못한다면, deadlocks, race conditions이 발생할 수 있습니다.
네트워크 프로그래밍에서의 동시성과 병렬성
- 네트워크 프로그래밍에서는 여러 클라이언트 요청을 처리하거나 다양한 I/O 작업을 동시에 수행하여 애플리케이션의 성능과 응답성을 크게 향상시킬 수 있습니다.
- 네트워크 프로그래밍에서의 동시성은 동일한 애플리케이션 프로세스 내에서 여러 네트워크 작업을 동시에 처리하는 것을 의미합니다.
- 여러 클라이언트 연결 관리, 비동기 데이터 전송, I/O 작업 처리와 같은 작업에 필수적입니다.
- 네트워크 프로그래밍에서의 병렬성은 여러 작업을 동시에 실행하는 것을 의미하며, 여러 코어 및 프로세서에서 수행됩니다.
- 네트워크를 통해 수신된 대량의 데이터를 처리하여 CPU 사용량이 높은 계산을 수행하는 것과 같은 작업에 유용합니다.
Thread
- 자바에서 동시성의 기본 단위
- 프로그램 내에서 특정 작업을 실행하는 경량화된 프로세스
Thread의 2가지 생성 방식
1. Thread class를 상속받은 후 run() 메서드 오버라이딩
class MyThread extends Thread {
public void run() {
// Code that runs in the new thread
}
}
2. Runnable Interface 구현 후, Thread 객체에 전달
class MyRunnable implements Runnable {
public void run() {
// Code that runs in the new thread
}
}
Thread myThread = new Thread(new MyRunnable());
myThread.start();
Thread Safety
- 동시성 프로그래밍의 중요한 측면으로서, 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어지도록 프로그램의 실행에 문제가 없는 것을 의미합니다.
- 이를 어길 경우, 위에서 설명드린 race condition, deadlock, data corruption 등이 발생할 수 있습니다.
Thread Safety를 얻기 위해 자바에서 제공하는 메커니즘
1. Synchronized Methods and Blocks
- 해당 키워드를 적용하면, 한 번에 하나의 스레드만 동기화된 메서드나 블록에 접근할 수 있습니다.
public synchronized void synchronizedMethod() {
// Thread-safe code
}
2. Volatile Variable
- 해당 키워드를 활용할 경우, 변수가 변경되었을 때 다른 스레드에서 즉시 표시되는 것을 보장합니다.
Synchronization
- 여러 스레드가 공유된 리소스에 접근하는 것을 제어하는 과정
- 데이터 불일치를 피하고, 스레드 안정성을 보장하기 위해 필수적입니다.
Java에서 제공하는 동시성 유틸리티
- Executors: 스레드의 수명 주기 관리를 추상화하고, 실행을 위해 작업을 제출하는 방법을 제공합니다.
- Concurrent Collections: 동시 접근에 더 나은 확장성을 제공하는 표준화된 Collection을 제공합니다. (ex) ConcurrentHashMap)
- Future and Callable: 해당 인터페이스들은 비동기 계산을 나타냅니다. Callable은 결과를 반환하고, Future는 Callable의 결과를 보유하고 있습니다.
- Locks: java.util.concurrent.locks 패키지는 synchronized methods/block과 같은 내재된 locks보다 훨씬 더 유연한 locking mechanism을 제공합니다.
동시성 사례: 여러 클라이언트의 연결 관리 (with ThreadPool)
- 서버가 여러 클라이언트의 연결을 동시에 처리해야 할 때 동시성은 매우 중요합니다.
- 각 클라이언트의 연결을 별도의 스레드로 처리하여 서버가 동시에 관리할 수 있습니다.
- ex) ThreadPool을 사용하여 서버에서 들어오는 클라이언트의 연결을 관리
ExecutorService executor = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket clientSocket = serverSocket.accept();
executor.submit(new ClientHandler(clientSocket));
}
Asynchronous I/O Operations
- Java의 Non-blocking I/O( NIO)와 Asynchronous I/O(AIO)는 동시성 지향 API로, 효율적인 I/O 작업을 가능하게 하여 고성능 네트워크 애플리케이션에 이상적입니다.
- NIO: 비차단 작업을 위해 채널과 버퍼를 활용하여 단일 스레드가 여러 I/O 채널을 관리할 수 있습니다.
- AIO: 비동기 채널을 도입하여 스레드를 차단하지 않고도 확장성이 뛰어난 I/O 작업이 가능합니다.
병렬성 사례: Parallel Streams, Fork/Join Framework, ComparableFuture
1. Parallel Streams
- Parallel Stream은 데이터를 여러 세그먼트로 나누어 각 세그먼트를 병렬로 처리하기에 대용량 데이터세트에 대한 작업 속도가 크게 향상됩니다.
List<String> myList = Arrays.asList( "a1" , "a2" , "b1" , "c2" , "c1" );
myList.parallelStream()
.filter(s -> s.startsWith( "c" ))
.forEach(System.out::println); // 병렬 처리
2. Fork/Join Framework
- 더 작업 작업으로 분할된 후 병합되거나 결합되어 최종 결과를 생성할 수 있는 작업을 위해 설계되었습니다.
- ExecutorService Interface를 구현한 것으로, 스레드 간 부하 분산을 위해 work-stealing algorithm을 사용합니다.
class MyRecursiveTask extends RecursiveTask<Integer> {
@Override
protected Integer compute() {
// Divide task and/or compute
}
}
3. CompletableFuture
- Java8에서 소개된 비동기 프로그래밍을 위한 강력한 도구입니다.
- 이를 통해 Non-blocking 로직을 더욱 쉽게 작성하고, 관리하기 쉽고 깔끔한 방식으로 병렬 작업을 수행할 수 있습니다.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result2");
CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (r1, r2) -> r1 + " " + r2);
<참고 자료>
https://www.baeldung.com/cs/concurrency-vs-parallelism
https://medium.com/@AlexanderObregon/java-networking-concurrency-vs-parallelism-in-java-70e892e4efe3
'CS' 카테고리의 다른 글
[CS] Socket Communication (1) | 2025.09.26 |
---|---|
[CS] Call by Value, Call by Reference (1) | 2025.08.31 |
[CS] clustered Index, non-clustered Index (4) | 2025.08.27 |
[Java] Functional Interface (5) | 2025.08.26 |
[CS] JVM Memory Structure (6) | 2025.08.15 |