동기화
- 동기화 : 여러 스레드가 동시에 접근할 수 있는 자원에 대해 일관성 있고 안전한 접근을 보장하기 위한 메커니즘이다.
- 멀티스레드를 사용할 때 가장 주의할 점은 같은 자원(리소스)에 여러 스레드가 동시에 접근할 때 발생하는 동시성 문제이다.
- ex) 은행에서 출금을 동시에 하는 경우 잔액은 공유 자원이 되는데 이때 동시성 문제가 발생하면 잔액 보다 더 많은 돈을 출금하는 경우가 생겨버린다.
- 공유 자원 : 여러 스레드가 접근하는 자원
- 대표적인 공유 자원은 인스턴스의 필드(멤버 변수)이다.
- 멀티스레드를 사용할 때 공유 자원에 대한 접근을 동기화해서 동시성 문제가 발생하지 않도록 방지하는 것이 중요하다.
임계 영역 (critical section)
- 임계 영역 : 여러 스레드가 동시에 접근하면 데이터 불일치나 예상치 못한 동작이 발생할 수 있는 위험하고 중요한 코드 부분과 여러 스레드가 동시에 접근해서는 안되는 공유자원을 접근하거나 수정하는 부분을 의미한다.
- ex) 은행에서 출금 기능을 하는 부분이 임계 영역
잔액은 여러 스레드가 동시에 접근해서는 안되는 공유 자원이므로 출금 시 잔액을 검증하고 잔액의 계산을 완료하는 부분까지가 임계영역이다.
- ex) 은행에서 출금 기능을 하는 부분이 임계 영역
- 임계 영역은 한 번에 하나의 스레드만 접근할 수 있도록 안전하게 보호해야 한다.
- 자바에서 제공하는 synchronized 키워드를 통해 임계 영역을 보호할 수 있다.
synchronized
- synchronized : 자바에서 제공하는 synchronized 키워드를 사용하면 한 번에 하나의 스레드만 실행할 수 있는 코드 구간을 만들 수 있다.
- 한 번에 하나의 스레드만 실행할 수 있기 때문에 전체적으로 보면 성능이 떨어질 수 있다.
- synchronized 메소드와 synchronized 코드 블록으로 사용할 수 있다.
- synchronized를 사용하면 volatile을 사용하지 않아도 메모리 가시성 문제가 해결된다.
//synchronized 메소드
public synchronized boolean withdraw(int amount) {...}
//synchronized 코드 블록
synchronized (this) {
//임계 영역 코드
}
- synchronized 메소드
- 메소드 반환타입 앞에 synchronized 키워드를 넣어주면 메소드 자체가 임계 영역으로 지정되어 한 번에 하나의 스레드만 진입할 수 있게 된다.
- synchronized 코드 블록
- 메소드 단위가 아닌 특정 코드 블록에만 synchronized로 임계 영역을 보호할 수 있도록 할 수 있다.
- 메소드로 지정하는 경우에는 공유 자원이 사용되지 않는 부분들이 존재할 수 있고 성능을 최적화 하기 위해서 정말 필요한 부분에만 지정할 수 있도록 synchronized 코드 블록을 사용한다.
- synchronized(this)에서 소괄호 안에 들어갈 값은 락을 획득할 인스턴스의 참조이다.
- 모든 객체(인스턴스)는 내부에 자신만의 락(lock)을 가지고 있다.
- 모니터 락(monitor lock)이라고도 부른다.
- 스레드가 synchronized 블록에 진입하려면 반드시 해당 인스턴스의 락이 있어야한다!
- 락을 획득한 스레드는 synchronized 메소드에 진입할 수 있다.
- 메소드 실행이 끝나면 해당 스레드는 락을 반납한다.
- 락이 없는 스레드는 락을 획득할 때까지 BLOCKED 상태로 대기한다.
- 락을 획득한 스레드는 synchronized 메소드에 진입할 수 있다.
- BLOCKED 상태인 스레드는 락을 획득하기 전까지 무한정 대기하며 CPU 실행 스케줄링 큐에 들어가지 않는다.
- 락을 획득하는 순서는 보장되지 않는다.
class SynchronizedClass {
synchronized void put(){...}
synchronized void take(){...}
}
- 예제와 같이 한 클래스 내부에 synchronized 메소드가 2개 이상 존재하는 경우?
- 두 메소드 다 synchronized(this)와 같은 의미로 자신의 객체의 lock을 사용하는 synchonized 메소드다.
- 그렇기에 put()에 진입하는 동안 take()에는 진입할 수 없고, take()에 진입하는 동안 put()에 진입할 수 없다.
- 하나의 스레드만 실행할 수 있는 안전한 임계 영역은 가능한 최소한의 범위에 적용해야 성능에 영향을 덜 미친다.
- 멀티스레드 환경에서는 필수적인 기능이지만 과도하게 사용하면 성능 저하를 초래하므로 꼭 필요한 곳에만 사용해야 한다.
- 동기화를 사용함으로 해결할 수 있는 문제
- 경합 조건(Race condition) : 두 개 이상의 스레드가 경쟁적으로 동일한 자원을 수정할 때 발생하는 문제
- 데이터 일관성 : 여러 스레드가 동시에 읽고 쓰는 데이터의 일관성을 유지
- 클래스 변수, 인스턴스 변수는 공유될 수 있지만 지역 변수는 절대 공유될 수 없다.
- 지역 변수는 스택 영역에 생성되며 다른 스레드와 공유하지 않기 때문
- final 변수는 여러 스레드에서 접근하더라도 문제가 되지 않는다.
- final 변수는 한 번 값을 지정하면 변경할 수 없기 때문에 어떤 스레드에서도 값을 변경할 수 없기에 동시성 문제가 발생하지 않는다.
- 접근하는 것 자체는 문제가 되지 않고 변경할 때 문제가 발생하기 때문
- 자바에서 제공하는 synchronized의 장/단점
- 장점
- 프로그래밍 언어에 문법으로 제공
- 사용하기에 편리함
- 자동 잠금 해제 - synchronized 메소드, 블록의 실행이 완료되면 자동으로 락을 대기중인 다른 스레드의 잠금이 해제된다.
- 단점
- 무한 대기 - BLOCKED 상태의 스레드는 락이 풀릴 때까지 무한정 대기한다. 중간에 인터럽트하거나 특정 시간까지만 대기할 수 없다.
- 공정성 - 락이 돌아왔을 때 BLOCKED 상태인 여러 스레드 중 어떤 스레드가 락을 획득할지 알 수 없고 특정 스레드가 너무 오랜기간동안 락을 획득하지 못할 수 있다.
- 장점
- synchronized의 단점을 보완하기 위해 concurrent 패키지도 존재하지만 단순하고 편리하게 사용하기에는 synchronized가 좋다.
출처 : [인프런 김영한 실전 자바 - 고급편]
김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 강의 | 김영한 - 인프런
김영한 | 멀티스레드와 동시성을 기초부터 실무 레벨까지 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다?
www.inflearn.com
'Java > [인프런 김영한 실전 자바 - 고급편]' 카테고리의 다른 글
[인프런 김영한 실전 자바 - 고급편] 생산자 소비자 문제 1, 2 (0) | 2024.08.06 |
---|---|
[인프런 김영한 실전 자바 - 고급편] concurrent.Lock (0) | 2024.08.02 |
[인프런 김영한 실전 자바 - 고급편] 메모리 가시성 (0) | 2024.08.02 |
[인프런 김영한 실전 자바 - 고급편] 스레드 제어와 생명 주기 1, 2 (1) | 2024.08.02 |
[인프런 김영한 실전 자바 - 고급편] 스레드 생성과 실행 (1) | 2024.07.30 |