diff --git a/study/ch04.md b/study/ch04.md
new file mode 100644
index 0000000..2eda671
--- /dev/null
+++ b/study/ch04.md
@@ -0,0 +1,298 @@
+### 4.1 스레드 안전한 클래스 설계하기
+
+- 상태에 대한 캡슐화, 소유권에 대한 이야기
+- stateful한 객체의 동시 접근을 관리하기 위한 방법 고려(기본형, 참조형)
+ - 불변, lock, 스레드 독점 방법 등
+- 소유권 분리?
+ - ex) `ServletContext`
+ - 애플리케이션 전체 범위에서 공유되는 객체를 저장할 수 있음
+ - ServletContext 자체는 동기화를 보장해주지만, 안에 들어가는 객체들은 **사용자가 안전하게 공유**해야 한다.
+
+
+
+
+
+```java
+attributes.put(name, value); // 안전
+attributes.get(name) // 안전
+```
+
+
+
+**저장된 객체 자체의 동시성은 컨테이너가 보장하지 않음**
+
+→ 여러 서블릿이 꺼내서 동시에 사용하는 경우는 **개발자가 직접 동기화**
+
+```java
+// 안전
+context.setAttribute("userService", new UserService());
+UserService service = (UserService) context.getAttribute("userService");
+
+// 위험, 개발자가 동기화에 신경써야함
+service.updateUserData(); // 다중 스레드에서 호출될 수 있음
+```
+
+- `HttpSession`은 세션 복제나 패시베이션 과정에서 컨테이너가 직접 접근하므로 그 안의 객체들도 **스레드 안전**해야 합니다.
+ - [15.2. HttpSession Passivation and Activation | HTTP Connectors Load Balancing Guide | Red Hat JBoss Web Server | 1.0 | Red Hat Documentation](https://docs.redhat.com/en/documentation/red_hat_jboss_web_server/1.0/html/http_connectors_load_balancing_guide/clustering-http-passivation)
+ - 사용되지 않는 세션을 메모리에서 제거하고 영구 스토리지에 저장할 수 있음
+ - 스레드 A가 `HttpSession`에 mutable한 `ShoppingCart` 객체 변경 후 저장 →
+ 웹 컨테이너의 내부 스레드가 세션 복제를 위해 `ShoppingCart` 객체를 직렬화
+ → **일관되지않은 상태가 복제**
+ - HttpSession에 저장되는 객체는 자체적으로 thread-safe 하거나, 해당 객체를 변경하는 부분에 동기화를 해줘야한다.
+
+
+
+
+> HttpSession 클래스도 ConcurrentHashMap으로 동기화 보장
+
+- [Chapter 17. HTTP session state replication | HTTP Connectors Load Balancing Guide | Red Hat JBoss Enterprise Application Platform | 5 | Red Hat Documentation](https://docs.redhat.com/en/documentation/JBoss_Enterprise_Application_Platform/5/html/http_connectors_load_balancing_guide/clustering-http-state)
+- 특정 WAS에 저장된 객체(ShoppingCart)를 다른 WAS로 복제(replication) 할 수 있음 ⇒ 싱크 불일치 가능성
+
+- 정리
+ - HttpSession, ServletContext는 둘다 객체를 읽고 쓰는부분에 대한 동기화는 보장한다.
+ - **저장된 객체의 상태변경시 동기화전략을 고려해야하는건 사용개발자의 몫이다**.
+
+
+### 4.2 인스턴스 한정
+- 결국 데이터 공개범위와 thread-safe와 연관된 이야기
+ - 책 코드, mySet을 노출시키지 않는다.
+
+ ```jsx
+ @ThreadSafe
+ public class PersonSet {
+
+ @GuardedBy("this")
+ private final Set mySet = new HashSet();
+
+ public synchronized void addPerson(Person p) {
+ mySet.add(p);
+ }
+ public synchronized boolean containsPerson(Person p) {
+ return mySet.contains(p);
+ }
+
+ }
+ ```
+
+- 자바 라이브러리의 대표적인 예시
+ - 스레드 안전하지않은 `HashMap`, `ArrayList` 등의 스레드 안전성을 확보
+
+ ```jsx
+ List list = Collections.synchronizedList(new ArrayList<>());
+
+ // 안전
+ list.add("A");
+ list.get(0);
+ ```
+
+
+
+
+
+
+
+
+
+
+왜 굳이 mutex 변수를 썼을까? this라던가.. (락 획득을 캡슐화)
+- 일단, Synchronized를 블록단위로 쓰려면 객체가 필요
+- 외부 코드가 `this` 객체로 synchronized를 잡는 것을 방지하기 위해
+- this: 외부에서 락을 잡을 수 있어 의도치 않은 경합 발생 가능
+- mutex: 외부에서 접근 불가 — 락 충돌 위험 차단
+
+
+### 모니터패턴(monitorenter) , synchronized
+
+[VM Spec Compiling for the Java Virtual Machine](https://docs.oracle.com/javase/specs/jvms/se6/html/Compiling.doc.html#6530)
+
+
+
+
+
+```java
+public class SynchronizedMain {
+ private static final Object lock = new Object();
+
+ public static void main(String[] args) {
+ someMethod();
+ }
+
+ private static void someMethod() {
+ synchronized (lock) {
+ System.out.println("hello");
+ }
+ }
+}
+```
+
+
+
+
+
+인터프리터가 바이트코드를 실행하며 monitorenter 바이트코드가 C++의 InterPreterRuntime::monitorenter()를 호출한다.
+
+문서들에 바이트코드의 의미만 정의하고 ,어떤 C++ 코드를 호출하는지는 다루는지에 대한 부분을 못찾음. `monitorenter`가 락을 건다는 것까진 **공식적으로 명세**되어 있음
+>> monitor entry on invocation of a method is handled implicitly by the Java virtual machine's method invocation instructions. (JVM의 내부적 지시에 따라 처리된다)
+
+
+GPT
+바이트코드 → 인터프리터 처리되는 과정의 소스로 증거 찾을수 있다… monitorenter를 찾아보자
+https://github.com/openjdk/jdk/blob/jdk-17%2B35/src/hotspot/share/interpreter/interpreterRuntime.cpp#L622
+ObjectSynchronizer::enter를 호출하고 이는 C++ 코드를 호출한다.
+
+
+synchronized 동작(ObjectSynchronizer::enter_for을 찾아보자)
+https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/synchronizer.cpp
+
+
+매우 간단하게 요약하면..(JVM 경량모드가 아닌경우 가정)
+```java
+ObjectSynchronizer::enter_for(obj)
+ └─ enter_fast_impl(obj, lock, thread)
+ ├─ CAS로 obj의 mark word를 검사하고 락 시도
+ ├─ CAS 성공 → 경량 락 획득 → Synchronized 블록 내부로 진입
+ └─ CAS 실패 → return false
+ ↓
+ └─ inflate_for(thread, obj)
+ └─ ObjectMonitor 생성 또는 기존 모니터 획득
+ └─ inflate_impl()
+ └─ 무한루프, CAS를 하며 ObjectMonitor 생성 또는 기존 모니터 획득
+ └─ 다른스레드가 락을소유중이면 entryList에 들어가고 moniterexit가 호출되어 notify()를 통해 스레드를 깨우고 진입.
+```
+
+
+경량모드일때의 동작
+[jdk/src/hotspot/share/runtime/lightweightSynchronizer.cpp at master · openjdk/jdk](https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/lightweightSynchronizer.cpp)
+경량 모드인지 아닌지는 JVM이 런타임에 모드를 선택한다. (해당 Synchronized에대한 경합이 강한지 아닌지에 따라)
+
+
+### 4.3 스레드 안전성 위임
+- AtomicLong, CopyOnWriteArrayList, ConcurrentHashMap 등 thread-safe를 위임하는 것을 말함
+- 성공 예시 (각 이벤트 리스너를 CopyOnWriteArrayList에 위임하거나, AtomicLong, ConcurrentHashMap등에 위임
+
+ ```java
+ @ThreadSafe
+ public class DelegatingVehicleTracker {
+ private final ConcurrentMap locations;
+ private final Map unmodifiableMap;
+
+ // Point는 불변객체
+ public DelegatingVehicleTracker(Map points) {
+ locations = new ConcurrentHashMap<>(points);
+ // 불변객체이기에, deepCopy 할필요가 없음
+ unmodifiableMap = Collections.unmodifiableMap(locations);
+ }
+
+ public Map getLocations() {
+ return unmodifiableMap;
+ }
+
+ public Point getLocation(String id) {
+ return locations.get(id);
+ }
+ }
+ ```
+
+- 위임 실패 예시(각 상태변수 간 관계가 있는경우)
+
+ ```java
+ public class NumberRange {
+ private final AtomicInteger lower = new AtomicInteger(0);
+ private final AtomicInteger upper = new AtomicInteger(0);
+
+ public void setLower(int i) {
+ //위험
+ if (i > upper.get())
+ throw new IllegalArgumentException(
+ "can't set lower to " + i + " > upper");
+ lower.set(i);
+ }
+
+ public void setUpper(int i) {
+ //위험
+ if (i < lower.get())
+ throw new IllegalArgumentException(
+ "can't set upper to " + i + " < lower");
+ upper.set(i);
+ }
+
+ public boolean isInRange(int i) {
+ return (i >= lower.get() && i <= upper.get());
+ }
+ }
+ ```
+
+- 내부 상태를 안전하게 공개해도 되는경우
+
+ ```java
+ @ThreadSafe
+ public class SafePoint {
+ @GuardedBy("this") private int x, y;
+
+ public SafePoint(int x, int y) { this.x = x; this.y = y; }
+
+ public synchronized int[] get() { return new int[] { x, y }; }
+
+ public synchronized void set(int x, int y) { this.x = x; this.y = y; }
+ }
+ ```
+
+
+### 4.4 스레드 안전하게 구현된 클래스에 기능추가
+
+| 방법 | 특징 | 위험 |
+| --- | --- | --- |
+| 클래스 확장 | 편리하지만 깨질 수 있음 | 부모 클래스 락 정책 변경시 위험 |
+| 클라이언트 측 락킹 | 외부에서 락을 맞추는 방식 | 락 정책 변경시 깨질 위험 |
+| 합성 | 안정적, 권장 | 다소 번거로움 |
+- 클래스 확장
+
+ ```java
+ @ThreadSafe
+ public class BetterVector extends Vector {
+ // 추가기능
+ public synchronized boolean putIfAbsent(E x) {
+ boolean absent = !contains(x);
+ if (absent){
+ add(x);
+ }
+ return absent;
+ }
+ }
+ ```
+
+- 클라이언트 측 락킹
+
+ ```java
+ @ThreadSafe
+ public class ListHelper {
+ public List list = Collections.synchronizedList(new ArrayList());
+
+ public synchronized boolean putIfAbsent(E x) {
+ boolean absent = !list.contains(x);
+ if (absent)
+ list.add(x);
+ return absent;
+ }
+ }
+ ```
+
+
+### 4.5 문서화
+
+## ✅ 문서화해야 할 것들
+
+- **클라이언트를 위한 스레드 안전 보장 내용**
+- 문서화 타이밍은 설계 당시가 가장 좋음
+
+## ✅ 동기화 정책 설계 시 결정해야 할 것들
+
+- 어떤 변수는 `volatile`로 할지
+- 어떤 변수는 락으로 보호할지
+- 어떤 락이 어떤 변수를 보호하는지
+- 어떤 변수는 불변으로 만들지
+- 어떤 연산은 원자적으로 처리해야 하는지
+
+## ✅ 안 좋은 현실
+- 많은 Java 명세(예: Servlet, JDBC)는 스레드 안전 보장이나 요구 사항을 거의 문서화하지 않음
+- 그래서 개발자는 어쩔 수 없이 **추측**해야 하는 상황에 놓임