skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

GOF 디자인 패턴 #36

Open skarltjr opened 2 years ago

skarltjr commented 2 years ago

싱글톤 패턴

화면 캡처 2021-10-18 172008

서로 다른 인스턴스이니까 true가 나온다


### 2. `new`를 사용하면 싱글톤을 만족할 수 없다. new를 사용하지 않으려면? 
- `private`
- ![화면 캡처 2021-10-18 173138](https://user-images.githubusercontent.com/62214428/137696299-cda7feca-2fb8-453b-8df8-37f5b2ba59ef.png)

public class Main { public static void main(String[] args) throws IOException { Settings settings = Settings.getInstance(); Settings settings1 = Settings.getInstance(); System.out.println(settings!=settings1); } }

- 이 또한 내부에서 `new`를 사용하기 때문에 settings 와 settings 1은 다르다
- 우리는 이 두 인스턴스가 같기를 바란다

### 3. 두 인스턴스가 같길 바란다는 목적을 달성해보자
- ![화면 캡처 2021-10-18 173645](https://user-images.githubusercontent.com/62214428/137697167-cabfc4a9-dc81-4440-8df6-0ed0a5f81949.png)
- 이제 두 인스턴스는 항상 같다!!

### 4. 그럼 싱글톤을 완성한것인가? 
- `아주 심각한 문제`
- `multithread`환경에서 과연 이 코드가 안전할까?
- ![화면 캡처 2021-10-20 115512](https://user-images.githubusercontent.com/62214428/138020621-90631a50-6e27-45a9-971d-12cd4dc58a9c.png)
- `파란색` 스레드가 `instance == null`을 체크하고 null이여서 `instance= new Settings()`에 진입하고 아직 새로운 인스턴스를 생성하지 않았는데 그 때 동시에 `빨간색` 스레드도 `if (instance == null)`을 체크하고 `instance= new Settings()`을 수행하게 된다면?
- 두 개의 인스턴스가 다르다. 그럼 싱글톤이 아니다.

### 5. 멀티쓰레드에서 안전하게 싱글톤을 완성하는 방법을 알아보자★
#### 1. synchronized 키워드 사용
![화면 캡처 2021-10-20 120019](https://user-images.githubusercontent.com/62214428/138021082-22f7383a-3776-4323-ae02-53a2bbbc9192.png)
- 문제점 : getInstance가 실행될 때 마다 동기화 관련 메커니즘이 실행되기 때문에 성능이슈가 있을 수 있다.
- 매 번 lock을 걸고 풀고 하는 과정이 있기 때문

public class Main { public static void main(String[] args) { MyThread myThread1 = new MyThread(1); MyThread myThread2 = new MyThread(2);

    myThread1.start();
    myThread2.start();

}

}

class MyThread extends Thread { public Settings settings; private int num; public MyThread(int num) { this.num = num; }

@Override
public void run() {
    for (int i = 0; i < 10000; i++) {
        settings = Settings.getInstance();
        System.out.println(settings + " thread " + num);
    }
}

}

algo.Settings@74719816 thread 2 algo.Settings@74719816 thread 1 algo.Settings@74719816 thread 2 algo.Settings@74719816 thread 1 .....

#### 2. 이른 초기화  / eager initialization
![화면 캡처 2021-10-20 120431](https://user-images.githubusercontent.com/62214428/138021474-ca604fb1-fbef-428f-834e-e8a15f5d48ce.png)
- 클래스가 로딩되는 시점에서 static한 필드들이 초기화되기 때문에  `INSTANCE`는 미리 만들어지고
- 그렇기 때문에 나중에 여러 스레드가 접근해도 결국 동일한 인스턴스를 return해주는 것 
- 즉 스레드 세이프하다
- 다만 `미리 만들어둔다는게 단점일 수 있다`
- 만약 이 인스턴스가 매우크다. 그런데 만들어놓고 안쓰는 경우가 생긴다면?

#### 3. double checked locking
- 결론적으로 나는 `미리 만들어두지 않고` 그러나 `synchronized` 성능 이슈를 최소화하고 싶다.
- ![화면 캡처 2021-10-20 122145](https://user-images.githubusercontent.com/62214428/138022963-ca37a670-ea33-46af-928a-064b03b18297.png)
- 빨간색 스레드가 먼저` if (instance == null)`하고 들어가서 lock을 잡고 한 번 더 `if (instance == null)`한 후 `new Settings`로 인스턴스를 생성한다
- 이 때 파란색 스레드가 1번 `if (instance == null)`을 하고 와도 `synchronized`. 즉 빨간색 스레드가 `lock`을 잡고 있기 때문에 그 다음으로 넘어가지 않고 기다려야한다.
- 주의 `volatile` 키워드가 자바 1.5 이상부턴 필요하다

1,2번과 비교해보자 ㄱㅣ본적으로 ★ 미리 만들어두는 것이 아니라 필요할 때 생성한다 -2 번 개선 synchronized의 위치를 보자. 먼저 if (instance == null)체크로 한 번 걸러준다. 즉 한 번 걸러준 다음에 synchronized 매커니즘이 동작한다. 그래서 성능 이슈를 조금 더 줄일 수 있다 - 1번 개선


----------------

사실 3번은 매우 복잡한 코드다.

### 권장방법 1. static inner class
![화면 캡처 2021-10-20 122437](https://user-images.githubusercontent.com/62214428/138023359-65e8b474-ddbe-430e-b27b-624977b16248.png)
- 이렇게 되면 필요할 때 생성 lazy init★
- 멀티스레드에서도 안전하다.

멀티스레드에서도 안전하다. ? 왜? static ★ static 필드들은 이미 클래스 로딩 시점에 생성되어 필드에 값이 채워져있다 그러니 getInstance()를 동시에 부르더라도 새로이 생성되는 것이 아니라 이미 만들어진 인스턴스를 부르고 그렇게 되면 절대 동시에 서로 다른 인스턴스가 생성될 수 없다

skarltjr commented 2 years ago

방학 때 정리하기