[Java] Synchronization - 조윤호
공유 자원에 대한 작업의 단위가 더 이상 쪼갤 수 없는 하나의 연산인 것 처럼 동작하는 것
CPU는 각각 메모리에 대한 캐시를 갖고 있고, writeback 방법(메모리에 직접 수정하지 않고, 캐시에 1차 수정 후 메모리에 일괄 수정하는 방식)을 사용한다.
일반적인 변수의 경우 한 cpu에서의 수정은 다른 cpu에 바로 반영되지 않는다(캐싱 효율을 위해).
volatile
로 변수를 선언하여 가시성 확보 -> 수정된 데이터가 바로 flush(캐시->메모리로 데이터 이동)된다.
하지만, 원자성 문제 (동시에 같은 값을 읽어다 증가시키고 flush하는...)로 인해 가시성이 100% 보장되지는 않음
Mutual exclusion queue에 프로세스 대기, 순차적으로 임계영역 접근
상호 배타, 실행순서 정렬 제공
// 메서드 전체를 임계 영역으로 설정하기
�public synchronized void mySyncMethod () ... {
...
}
// 특정한 영역을 임계 영역으로 설정
synchronized (객체의 참조변수 ) {
...
}
성능 개선
임계영역의 사용을 최소화할것
공유자원,전역변수 되도록 사용하지 않을 것
단점
큐에서의 대기로 인한 성능저하 발생
데드락 발생 가능
wait() : 객체의 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣는다.
notify() : waiting pool에서 대기중인 쓰레드 중 하나를 깨운다.
notifyAll() :waiting pool에서 대기중인 모든 쓰레드를 깨운다.
다른 스레드의 작업여부와 상관없이 자신의 작업을 수행
CAS 알고리즘 (Compile and Set)
연산 전의 값을 저장(=기댓값) 후 연산 수행
연산 결과를 저장하기 전, 기댓값과 기존 자원의 값을 비교
만약 다르다면 수정하지 x, False 반환
atomic 타입 변수 = CAS + Volatile
�private AtomicInteger value = new AtomicInteger (0 );
volatile 변수로 선언됨
compareAndSet 메소드 실행
public class AtomicInteger extends Number implements Serializable {
private static final long serialVersionUID = 6214790243416807050L ;
private static final Unsafe U = Unsafe .getUnsafe ();
private static final long VALUE ;
private volatile int value ;
//...
public final boolean compareAndSet (int expectedValue , int newValue ) {
return U .compareAndSetInt (this , VALUE , expectedValue , newValue );
}
}