e.g.
public class Counter { private int count; public int increase() { return ++count; // Thread-safe 하지 않은 연산 } }
위와 같은 코드가 있을 때,
1. read(count 값 읽기) -> 2. modify (count값 수정) -> 3. write (count값 저장)
과정에서 여러 Thread가 공유 자원 (count)으로 접근할 수 있으므로 동시성 문제 발생📍 동시성 문제는 결국 여러 스레드가 공유 자원 (count 변수)에 접근하기 때문에 발생하는데 동시성 문제를 해결하기 위해 count 변수로 접근하는 스레드를 제어해야 함
public class Counter {
private Object lock = new Object();
private int count;
public int increase() {
synchronized(lock) {
return ++count;
}
}
}
lock
이라는 Object
의 instance
를 사용하여 스레드가 동시에 count 변수에 접근하지 못하도록 제어. increase()
메서드는 한 번에 한 스레드만 실행할 수 있음. 한 스레드가 먼저 락을 획득한 경우, 다른 스레드는 기다려야 함. public class Counter {
private int count;
public synchronized int increase() {
return ++count;
}
}
synchronized
키워드를 붙여주는 것으로 대신할 수 있음synchronized
블록 단위로 락의 획득/ 해제가 일어나므로 구조적이라고 함자바의 고유락은 재진입이 가능
재진입이 가능하다는 것은 락의 획득이 호출 단위가 아닌 스레드 단위로 일어난다는 것을 의미
이미 락을 획득한 스레드는 같은 락을 얻기 위해 대기할 필요가 없음 -> synchronized
블록을 만났을 때, 대기없이 통과 가능
public class Reentrancy {
public synchronized void a() {
System.out.println("a");
b();
}
public synchronized void b() {
System.out.println("b");
}
public static void main (String[] args) {
new Reentrancy().a();
}
}
b가 synchronized로 선언되어 있더라도, a 진입시 lock을 획득하였기 때문에 b를 호출할 수 있음
Structure Lock
과 Reentrant Lock
은 visibility
보장http://happinessoncode.com/2017/10/04/java-intrinsic-lock/
https://gyoogle.dev/blog/computer-language/Java/Intrinsic%20Lock.html