728x90
반응형
Synchronized
- 키워드를 메서드나 블록에 적용해 해당 코드 블록에 접근할 때 하나의 스레드만 접근할 수 있도록 보장
- 락을 잡고 있는 동안 다른 스레드들은 해당 자원을 사용하지 못하기 때문에 경합이 발생하여 성능 저하가 일어날 수 있음
- 락을 과도하게 사용하면 데드락 문제도 발생할 수 있음
ReentrantLock
- Lock 인터페이스를 구현한 클래스. 락을 명시적으로 관리할 수 있게 함
- 락을 획득할 때 타임아웃 설정이나 중단할 수 있는 기능을 제공함
- synchronized보다 여러 조건을 처리하는데에 유리
- 락을 획득하는데 실패할 경우 재시도를 하거나 특정 시간내에 락을 획득하지 못하면 다른 처리를 할 수 있음
Lock lock = new ReentrantLock();
lock.lock();
try {
// 작업 수행
} finally {
lock.unlock();
}
ReadWriteLock
- 읽기와 쓰기를 분리하여 읽기 작업이 동시에 여러 스레드에서 이루어지도록 허용하는 방법. 쓰기 작업은 하나의 스레드만 접근할 수 있음
- 읽기 작업이 많고 쓰기 작업이 적은 경우 성능 향상에 도움이 됨
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
try {
// 읽기 작업
} finally {
lock.readLock().unlock();
}
lock.writeLock().lock();
try {
// 쓰기 작업
} finally {
lock.writeLock().unlock();
}
Atomic Classes
- 원자적인 연산을 지원하는 Atomic 클래스 제공
- AtomicInteger, AtomicLong, AtomicReference 등을 사용하면 동기화 없이 원자적인 값을 업데이트 할 수 있음
- 내부적으로 CAS(Compare And Swap) 알고리즘을 사용하여 동기화 문제를 해결
- 단일 변수에 대한 동기화만 제공하고 복잡한 객체나 다중 변수를 다룰때는 적합하지 않음
AtomicInteger count = new AtomicInteger();
count.incrementAndGet(); // 동기화 없이 원자적으로 값 증가
ForkJoinPool
- 병렬 작업을 처리하는데 최적화된 Pool
- 작업을 분할하여 병렬로 처리. 재귀적인 작업에 유리함
- 분할-정복 전략을 사용하여 큰 작업을 작은 작업으로 나누고 그 결과를 합치는 방식으로 동시성 관리
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.submit(() -> {
// 분할-정복 알고리즘 예시: 배열을 분할하여 각 부분을 병렬로 처리
});
forkJoinPool.shutdown();
Executors
- 스레드 풀을 관리하고 스레드 생성을 효율적으로 할 수 있게 도와줌
- ExecutorService는 비동기 작업을 처리할 수 있는 API를 제공. 스레드 풀에서 동시 작업을 관리
- 멀티스레딩 작업에 적합. 여러 독립적인 작업을 동시 처리
ExecutorService executor = Executors.newFixedThreadPool(10); // 10개의 스레드를 가진 풀 생성
executor.submit(() -> {
System.out.println("작업 1");
});
executor.submit(() -> {
System.out.println("작업 2");
});
executor.shutdown(); // 작업이 끝난 후 스레드 풀을 종료
CompletableFuture
- 비동기 프로그래밍을 위한 도구. 여러 작업을 병렬로 실행하고 그 결과를 조합함
- 비동기 작업의 결과를 기다리며 다른 작업을 수행할 수 있음
CompletableFuture.supplyAsync(() -> {
// 비동기 작업
return "Result";
}).thenAccept(result -> {
// 결과 처리
});
ExecutorService vs ForkJoinPool
특성 ExecutorService ForkJoinPool
목적 | 일반적인 작업을 처리하는 스레드 풀 | 재귀적이고 병렬화 가능한 분할-정복 작업 처리 |
작업 처리 방식 | 큐에 들어온 작업을 스레드 풀에서 처리 | 큰 작업을 작은 작업으로 나누어 병렬 처리 후 결과 합침 |
작업 스케줄링 | 큐에서 작업을 하나씩 처리 | 워커 스레드가 작업을 훔쳐서 처리(워크 스티알링) |
적합한 사용 사례 | 일반적인 멀티스레딩 작업, I/O 작업 | 분할-정복 알고리즘, 재귀적이고 계산 집약적인 작업 |
성능 최적화 | 다양한 스레드 풀 전략을 통해 최적화 가능 | 워크 스티알링을 통해 비효율적인 스레드 처리를 피함 |
에러 처리 | 작업의 결과를 쉽게 처리할 수 있는 메서드 제공 | 재귀적으로 분할된 작업을 처리할 때 관리가 필요함 |
728x90
반응형
'Java & Kotlin' 카테고리의 다른 글
JVM (0) | 2025.01.26 |
---|---|
Garbage Collection (0) | 2025.01.26 |
Java & Kotlin 장단점 (0) | 2025.01.25 |
PriorityQueue (우선순위 큐) (0) | 2023.07.07 |
Kotlin? (0) | 2022.08.28 |