Catch - Multi thread (feat. ORA-00060)
서론

위의 사진을 잘 관람한다면 현재의 무한경쟁시대에 돌입하듯 동그란 자원을 서로 못 가져가서 안달이다.
이렇게 서로 잡고 안 놔주니 자원이 교착상태가 일어나게 된다.
그렇담, 치열하게 전투를 할 수밖에 없어진 명백한 사유에 대하여 알아보자.
Reason

위의 그림은 하드웨어 아키텍처를 단순화한 다이어그램이다.
CPU는 두 개의 코어를 나타낸 것이고, 각 CPU는 주어진 시간 내로 하나의 스레드를 실행할 수 있다.
즉, 스레드의 경우 CPU당 스레드가 애플리케이션 내에서 동시에 실행될 수 있음을 의미한다.
그리고 변수를 사용할 때 Main Memory에 도달하기 전 CPU Cache에 우선적으로 들락거려야 한다.
즉, 캐시에 존재하는 데이터를 읽는다는 Sound인데, 아래와 같이 개체와 변수가 컴퓨터의 다양한 메모리 영역에 저장되어 사용하는 경우, 위에서 보았던 피 튀기는 자원무한경쟁의 시대가 개막을 하게 되는 것이다.

Solution
synchronized
하나의 스레드만 이용이 가능한 '임계 영역'을 코드단에서 synchronized를 이용하여 설정을 할 수 있다.
하나의 스레드가 해당 임계 영역에 당도하면 그 스레드는 'Lock'을 수여받는데, 무언가를 수행 중인 스레드가 Lock을 반납해야만 다른 스레드가 임계 영역을 사용할 수 있다. 이를 동기화라고 한다.
메서드 단위, 코드 블록 단위 등 원하는 단위로 나누어 사용이 가능하다.
사용법 외에 스레드 처리에 대한 예시는 굳~이 하지 않겠다.
아래의 예시와 같이 사용법은 간단하다.
// 1. 메소드 전체를 임계영역화
public synchronized void batchDosa() {
}
// 2. 특정영역만 임계영역화
public void moDosa() {
synchronized (){
}
}
Key Point: 임계 영역, Lock
volatile
앞서 데이터 교환이 일어나려면 CPU 메모리의 캐싱된 값을 가져온다고 하였다.
반면 volatile는 synchronized 와는 다르게 캐싱된 값이 아닌,
항상 최신의 값을 가지도록 메인 메모리의 값을 참조하고 'Lock free'인 관계로 성능면에서 유리한 장점이 있다.
그러나 단지 최신의 값을 가질 뿐이지 모든 동기화를 해결할 수는 없다.
단, 단순한 스레드 간의 통신이나 원자적 연산은 동기화가 잘 될 것이다.
아래의 예시와 같이 사용법은 간단하다.
// Before
boolean catchThread = false;
// After
volatile boolean catchThread = false;
Atomic
이름만 들어도 원자성은 기필코 확보할 것만 같은 이름이다.
앞서 두 개의 경우는 예약어지만, Atomic은 java.util.concurrent.atomic 패키지에 정의된 클래스이다.
synchronized와 유사하지만 Lock이 아닌, 저수준 CPU 연산 기술인 Compare-And-Swap(CAS) 알고리즘을 사용한다.
이는 메인 메모리에 저장된 값과 CPU 캐시에 저장된 값을 비교하고, 두 값이 같은 경우에만 데이터를 수정한다.
synchronized의 장점인 가시성과 원자성을 챙기면서도 Lock도 없어 성능까지 챙길 수 있는 아주 영특한 녀석이다.
아래의 예시와 같이 사용법은 간단하다.
// Before
private boolean soJuNaMaSyo = false;
// After
private AtomicBoolean soJuNaMaSyo = new AtomicBoolean();
아래의 URL에 Atomic 패키지의 다양한 사용법 및 예제가 있으니, 더 많은 사용법을 원한다면 URL을 참조하면 될 것이다.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
java.util.concurrent.atomic (Java Platform SE 8 )
Class Summary Class Description AtomicBoolean A boolean value that may be updated atomically. AtomicInteger An int value that may be updated atomically. AtomicIntegerArray An int array in which elements may be updated atomically. AtomicIntegerFieldUpdate
docs.oracle.com
결론: 배치도사 무도사
필자는 이렇게 생각한다.
배치 수행 시 Read시간이 경과되는 Where절이 존재하는 배치는 싱글 스레드로 돌리는 것이 보다 안전하다.
본 바와 같이 동기화를 사용하여 자원동행을 하는 것은 기본. DML별로 디렉터리 or URL로 쪼개서 관리하는 법도 있다.
실시간 배치, 특히 DML에서 C, D가 동시에 존재하는 경우 경합이 될 소지가 높으니 이의 경우에는 D를 제거하고, DB 파티션을 데이터에 맞게 분할을 하여 삭제를 도모하는 것도 있으니 여러 방면으로 골똘히 아이디어를 창출해 보면 좋을 것이다.