디자인 모드 해석 6 단일 모드
며칠 저녁 의 분투 끝 에 여섯 번 째 편 까지 썼 습 니 다. 이것 도 마지막 으로 상세 하 게 쓴 디자인 모델 입 니 다. 단일 모델 을 소개 한 후에 나머지 몇 가지 모델 은 자주 사용 하지 않 는 디자인 모델 입 니 다. 그러면 이렇게 상세 하 게 소개 하지 않 을 것 입 니 다.
단일 모드
단일 모델 은 가장 간단 하지만 가장 어 려 운 디자인 모델 이 라 고 할 수 있다. 간단 한 것 은 이해 하기 쉽 기 때문이다. 어 려 운 것 은 완벽 한 단일 모델 을 진정 으로 쓰 려 면 전문 지식 이 많이 필요 하고 단일 사례 를 쓰 는 방식 도 매우 많 기 때문이다.일상적인 프로 그래 밍 에서 우 리 는 데이터베이스 연결 탱크, 캐 시 대상 등 단일 모드 를 자주 사용한다. 이런 대상 이 두 개 나타 나 면 프로그램 에 큰 피 해 를 줄 수 있다. 그러나 우리 프로그램의 다 중 루틴 특성 때문에 다 중 루틴 운행 환경 에서 고성능 과 안전 한 단일 루틴 을 불 러 오기 가 쉽 지 않다.
다음 에 절차 에 따라 하나의 사례 모델 을 실현 합 니 다. 먼저 일반적인 사고 논리 에 따라 우 리 는 한 곳 에 이 사례 의 정례 화 대상 을 저장 하고 얻 기 전에 판단 을 합 니 다. 비어 있 으 면 초기 화 되 고 비어 있 지 않 으 면 이 대상 으로 돌아 갑 니 다. 이렇게 보면:
public final class DataSourcePool {
private static DataSourcePool pool;
private DataSourcePool() {}
public static DataSourcePool getPool() {
if (Objects.isNull(pool)) {
pool = new DataSourcePool();
}
return pool;
}
}
네, 괜찮아 보 입 니 다. 그런데 프로그램 이 다 중 스 레 드 환경 에서 실행 된다 면 문제 가 있 습 니 다. 동시 다발 할 때 모두 가 판단 할 때 비어 있 을 수도 있 습 니 다. 그리고 모두 실례 화 를 하면 모두 가 같은 대상 이 아 닐 수도 있 습 니 다. 그러면 동기 화 자 물 쇠 를 추가 하 는 것 이 어 떻 습 니까?
public final class DataSourcePool {
private static DataSourcePool pool;
private DataSourcePool() {}
public synchronized static DataSourcePool getPool() {
if (Objects.isNull(pool)) {
pool = new DataSourcePool();
}
return pool;
}
}
동기 화 키 워드 를 방법 에 추가 할 수 있 습 니 다. 네, 이것 은 하나의 사례 라 고 보장 할 수 있 습 니 다. 이 방법 으로 동기 화 체 제 를 실 시 했 기 때문에 하나의 스 레 드 만 이 방법 에 들 어 갈 수 있 습 니 다. 그러나 이 방법 은 우리 프로그램의 병목 이 될 것 입 니 다. 이것 은 안 됩 니 다. 그러면 방법 내부 에 추가 할 수 있 습 니까?
public final class DataSourcePool {
private static DataSourcePool pool;
private DataSourcePool() {}
public static DataSourcePool getPool() {
if (Objects.isNull(pool)) {
synchronized (DataSourcePool.class) {
if (Objects.isNull(pool)) {
pool = new DataSourcePool();
}
}
}
return pool;
}
}
이렇게 하면 비교적 좋아 보 입 니 다. 첫 번 째 초기 화 전에 모두 가 동시 방문 할 때 만 자물쇠 키 부분 을 촉발 하 는 코드 가 나타 납 니 다. 초기 화 된 후에 모두 가 단일 대상 을 직접 받 을 수 있 습 니 다. 그러나 이것 은 다 중 스 레 드 환경 에서 반드시 안전 합 니까?사실 안전 하지 않 습 니 다. 다 중 스 레 드 의 명령 재 정렬 원 리 를 알 면 이 코드 가 실제 적 으로 안전 하지 않다 는 것 을 이해 할 수 있 습 니 다. cpu 는 우리 의 명령 을 수행 할 때 최종 결과 에 영향 을 주지 않 는 전제 에서 최적화 와 명령 재 정렬 을 할 것 입 니 다.우리 의 모든 줄 코드 는 하나의 원자 명령 으로 컴 파일 되 고 원자 명령 이 야 말로 cpu 가 명령 을 수행 하 는 가장 작은 단위 이 며, 원자 명령 만 이 분할 할 수 없 는 것 이다. 예 를 들 어
int a ;
a = 1;
이 두 명령 은 바로 두 개의 원자 명령 이다. 첫 번 째 명령 은 변수
a
에 메모리 공간 을 분배 하 는 것 이 고 두 번 째 명령 은 a
에 값 을 부여 하 는 것 이다.그 다음 에 이 명령 은 원자 명령 이 아 닙 니 다. int a = 2 ;
이 명령 은 원자 조작 이 아 닙 니 다. 명령 은 먼저 a 변 수 를 정의 한 다음 에 a 에 값 을 부여 하기 때 문 입 니 다. 이 점 은 자바 바이트 코드 를 컴 파일 해서 증명 할 수 있 습 니 다.public class Test {
public static void main(String[] args) {
int a ;
a = 1 ;
int b = 3 ;
}
}
javap -verbose Test.class
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=1
0: iconst_1
1: istore_1
2: iconst_3
3: istore_2
4: return
LineNumberTable:
line 13: 0
line 14: 2
line 15: 4
위의 0, 1, 2, 3 은 우리 의 명령 입 니 다. 이 네 가지 명령 을 자세히 보 세 요.
iconst_3
은 int 형 상수 치 3 을 대표 합 니 다. 그리고 istore2. 스 택 꼭대기 int 형 수 치 를 세 번 째 부분 변수 에 저장 합 니 다. 즉, 이 절 차 는 두 가지 명령 으로 나 뉜 다 는 것 입 니 다.다시 위의 코드 pool = new DataSourcePool();
로 돌아 가면 우 리 는 이곳 이 사실은 두 부분 으로 나 눌 수 있 고 일 부 는 실례 화 DataSourcePool
한 다음 에 이 대상 의 주 소 를 pool
에 할당 하 는 것 을 볼 수 있다.그러나 시스템 이 명령 을 다시 정렬 하면 대상 의 주소 할당 pool
을 초기 화하 지 못 하고 스 레 드 전환 이 나타 날 수 있 습 니 다. 다른 스 레 드 가 가 져 왔 습 니 다 pool
. 주 소 를 부 여 했 기 때문에 이때 pool
비 어 있 지 않 습 니 다. 이 스 레 드 를 받 은 후에 사용 하면 오류 가 발생 할 수 있 습 니 다. 대상 이 초기 화 되 지 않 았 기 때 문 입 니 다.물론 이런 일 은 매우 극한 적 인 상황 이지 만 반드시 잘못 보고 하지 않 을 것 이 라 고 보장 할 수 없 기 때문에 이런 상황 을 피하 기 위해 우 리 는 변수 volatile
키 워드 를 증가 시 킬 수 있다.public final class DataSourcePool {
private volatile static DataSourcePool pool;
private DataSourcePool() {}
public static DataSourcePool getPool() {
if (Objects.isNull(pool)) {
synchronized (DataSourcePool.class) {
if (Objects.isNull(pool)) {
pool = new DataSourcePool();
}
}
}
return pool;
}
}
키 워드 를 추가 하면 대상 에 대한 명령 정렬 을 금지 할 수 있 습 니 다.한편,
volatile
키 워드 는 실제 적 으로 메모리 장벽 을 이용 하 는 방식 으로 이 루어 진 것 이다. 즉, pool
변 수 를 위해 메모리 장벽 을 증가 하고 명령 을 실행 하기 전에 명령 을 읽 으 면 실행 할 수 없 으 며 다른 스 레 드 가 받 은 pool
대상 이 중간 상태 가 아 닌 것 을 확보한다.구체 적 인 volatile
과 happen - before 원칙 은 앞으로 의 글 에서 묘사 할 것 이다.위 와 같이 완벽 한 게으름뱅이 모델 의 한 예 이다.단일 사례 모델 을 실현 하 는 것 은 다른 방식 도 있 습 니 다. 예 를 들 어 정적 변수 에 직접 예화 시 켜 jvm 로 더 로 하여 금 우리 에 게 대상 의 단일 예 를 보장 하 게 합 니 다. 왜냐하면 jvm 은 클래스 의 로 딩 을 한 번 만 보장 하기 때 문 입 니 다.public final class DataSourcePool {
public final static DataSourcePool pool = new DataSourcePool();
}
final 키 워드 를 추가 해도 대상 값 이 바 뀌 지 않도록 확보 할 수 있다.인터넷 에서 도 매 거 를 통 해 단일 사례 를 실현 하 는 것 을 볼 수 있다. 이 방식 은 가장 안전 한 방식 이 라 고도 불 린 다. JVM 은 enum 이 반사 되 지 않도록 보장 하고 구조 기 방법 을 한 번 만 수행 할 수 있 기 때문이다. 일반적인 단일 사례 가 반 직렬 화 를 하면 새로운 대상 이 고 매 거 진 직렬 화 된 특수 처리 (직렬 화 할 때 자바 는 매 거 진 대상
name
에 불과 하기 때문이다.속성 이 결과 에 출력 되 고 반 직렬 화 될 때 java.lang.Enum
의 valueOf
방법 으로 이름 에 따라 매 거 진 대상 을 찾 습 니 다) 반 직렬 화 를 하 더 라 도 단일 대상 임 을 보증 할 수 있 습 니 다.public class DataSourcePool {
private DataSourcePool() {}
public static DataSourcePool getPool() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE;
private DataSourcePool singleton;
//JVM
Singleton() {
singleton = new DataSourcePool();
}
public DataSourcePool getInstance() {
return singleton;
}
}
}
public static void main(String[] args) {
DataSourcePool pool = DataSourcePool.getPool();
DataSourcePool pool2 = DataSourcePool.getPool();
System.out.println(pool == pool2);
}
실행 결과:
true
또한, 우리 의 spring 에 있 는 일부 bean 주해 도 기본 적 인 사례 입 니 다.
@Component
@Service
등 spring 은 bean 을 초기 화하 고 용기 에 저장 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.