자바 디자인 모델 투석 의 - 단일 예 (Singleton)

7872 단어
출처
원본 주소:http://blog.csdn.net/guolin_blog/article/details/8860649
머리말
방향 을 돌 렸 지만 그 전의 기초 가 너무 튼튼 하지 않 아서 많은 기술적 인 것들 이 잘 옮 기지 못 했 습 니 다. 물론 제 가 예전 에 접촉 면 이 너무 좁 았 던 것 과 관련 이 있 습 니 다.최근 안 드 로 이 드 개발 을 공부 하면 서 좋 은 글 을 많이 만 났 습 니 다. 기록 해 보 세 요.
원문 내용
소프트웨어 를 쓸 때 자주 인쇄 로그 기능 을 사용 해 야 합 니 다. 디 버 깅 과 포 지 셔 닝 문 제 를 도 울 수 있 고 프로젝트 가 출시 된 후에 도 데 이 터 를 분석 하 는 데 도움 을 줄 수 있 습 니 다.그러나 자바 가 원래 가지 고 있 는 System. out. println () 방법 은 실제 프로젝트 개발 에 사용 되 지 않 고 심지어 findbugs 등 코드 검사 도 구 는 System. out. println () 을 사용 하 는 것 이 bug 라 고 생각 합 니 다.
왜 자바 의 초보 신기 인 System. out. println () 이 진정한 프로젝트 개발 에 들 어가 면 버 려 질 까?사실 자세히 분석 하기 만 하면 너 는 그것 의 많은 폐단 을 발견 할 수 있 을 것 이다.예 를 들 어 제어 할 수 없 으 면 모든 로 그 는 프로젝트 가 출시 된 후에 정상적으로 인쇄 되 어 운행 효율 을 낮 출 수 있다.로 컬 파일 에 로 그 를 기록 할 수 없 거나 인쇄 가 삭제 되면 로 그 를 다시 찾 을 수 없습니다.또한 인쇄 된 내용 이 태그 구분 이 없 으 면 이 줄 로그 가 어떤 종류 에서 인쇄 되 었 는 지 판별 하기 어 려 울 것 입 니 다.
당신 의 leader 도 바보 가 아 닙 니 다. System. out. println () 의 여러 가지 단점 을 잘 알 고 있 습 니 다. 그래서 그 가 오늘 당신 에 게 준 임 무 는 로그 도구 류 를 만들어 더 좋 은 로그 기능 을 제공 하 는 것 입 니 다.하지만 리더 십 은 괜 찮 습 니 다. 처음부터 다양한 기능 을 가 진 로 그 를 만 들 지 못 했 습 니 다. 인쇄 단 계 를 제어 할 수 있 는 로그 도구 만 있 으 면 됩 니 다.
이 요 구 는 당신 에 게 결코 어렵 지 않 습 니 다. 당신 은 즉시 쓰기 시 작 했 고 첫 번 째 버 전 을 빨리 완 성 했 습 니 다.
public class LogUtil {
	public final int DEBUG = 0;

	public final int INFO = 1;

	public final int ERROR = 2;

	public final int NOTHING = 3;

	public int level = DEBUG;

	public void debug(String msg) {
		if (DEBUG >= level) {
			System.out.println(msg);
		}
	}

	public void info(String msg) {
		if (INFO >= level) {
			System.out.println(msg);
		}
	}

	public void error(String msg) {
		if (ERROR >= level) {
			System.out.println(msg);
		}
	}
}

이 종 류 를 통 해 로 그 를 인쇄 하려 면 level 의 단 계 를 제어 하면 인쇄 내용 을 자 유 롭 게 제어 할 수 있 습 니 다.예 를 들 어 현재 프로젝트 가 개발 단계 에 있 으 면 level 을 DEBUG 로 설정 하면 모든 로그 정보 가 인쇄 됩 니 다.프로젝트 가 출시 되면 level 을 INFO 로 설정 하면 INFO 및 이상 단계 의 로그 인쇄 만 볼 수 있 습 니 다.오류 로그 만 보고 싶다 면 level 을 ERROR 로 설정 할 수 있 습 니 다.개발 한 프로젝트 가 클 라 이언 트 버 전이 라면 로 그 를 인쇄 하지 않 으 려 면 level 을 NOTING 으로 설정 할 수 있 습 니 다.인쇄 할 때 호출 만 하면 됩 니 다:
new LogUtil().debug("Hello World!");

당신 은 잠시 도 지체 하지 않 고 이 도 구 를 당신 에 게 소개 한 leader 입 니 다. 당신 의 leader 는 당신 의 소 개 를 듣 고 말 했 습 니 다. "그래, 앞으로 모두 가 당신 이 쓴 이 도구 로 로 로 그 를 인쇄 할 거 야!"
그러나 얼마 지나 지 않 아 당신 의 leader 가 당신 을 찾 아 문 제 를 피드백 하 러 왔 습 니 다.그 는 이 도 구 는 사용 하기 좋 지만 인쇄 는 대상 을 구분 하지 않 는 다 고 말 했다. 로 그 를 인쇄 해 야 할 때마다 new 에서 새로운 LogUtil 을 만들어 야 한다. 메모리 가 너무 많이 차지 하 니 이 도 구 를 단일 모드 로 바 꿔 서 실현 하 기 를 바란다.
당신 은 당신 의 leader 가 말 한 것 이 매우 일리 가 있다 고 생각 합 니 다. 그리고 당신 도 이 기 회 를 틈 타 디자인 모델 을 사용 하 는 연습 을 하려 고 합 니 다. 그래서 당신 은 다음 과 같은 코드 를 썼 습 니 다 (ps: 여기 코드 는 제 가 직접 실현 한 것 입 니 다. 그리고 저 는 스 레 드 동기 화 문제 에 주의 하지 않 았 습 니 다).
public class LogUtil {
	private static LogUtil logUtilInstance;

	public final int DEBUG = 0;

	public final int INFO = 1;

	public final int ERROR = 2;

	public final int NOTHING = 3;

	public int level = DEBUG;

	private LogUtil() {

	}

	public static LogUtil getInstance() {
		if (logUtilInstance == null) {
			logUtilInstance = new LogUtil();
		}

		return logUtilInstance;
	}

	public void debug(String msg) {
		if (DEBUG >= level) {
			System.out.println(msg);
		}
	}

	public void info(String msg) {
		if (INFO >= level) {
			System.out.println(msg);
		}
	}

	public void error(String msg) {
		if (ERROR >= level) {
			System.out.println(msg);
		}
	}

	public static void main(String[] args) {
		LogUtil.getInstance().debug("Hello World!");
	}
}

먼저 LogUtil 의 구조 함 수 를 민영화 하면 new 키 워드 를 사용 하여 LogUtil 의 인 스 턴 스 를 만 들 수 없습니다.그 다음 에 sLogUtil 의 개인 정적 변 수 를 사용 하여 인 스 턴 스 를 저장 하고 공유 하 는 getInstance 방법 을 제공 하여 LogUtil 의 인 스 턴 스 를 가 져 옵 니 다. 이 방법 에서 sLogUtil 이 비어 있 으 면 new 에서 새로운 LogUtil 인 스 턴 스 를 내 고 그렇지 않 으 면 바로 sLogUtil 로 돌아 갑 니 다.이렇게 하면 메모리 에 LogUtil 의 인 스 턴 스 만 존재 할 것 이 라 고 보장 할 수 있다.단일 모드 완료!이 때 로 그 를 인쇄 하 는 코드 는 다음 과 같은 방식 으로 바 꿔 야 합 니 다.
LogUtil.getInstance().debug("Hello World");  

당신 은 이 버 전 을 리더 에 게 보 여 주 었 습 니 다. 그 는 보고 웃 으 며 "단일 모드 를 실현 한 것 처럼 보이 지만 bug 가 존재 합 니 다."라 고 말 했다.
너 는 의심 이 가득 하 다. 단일 모드 는 모두 이렇게 실현 되 지 않 니? 또 어떤 bug 가 있 을 까?
당신 의 leader 는 단일 모드 를 사용 하 는 것 은 메모리 에 하나의 인 스 턴 스 만 있 을 수 있 도록 하 는 것 입 니 다. 그러나 다 중 스 레 드 에서 로 그 를 인쇄 하 는 상황 을 고려 하고 있 습 니까? 아래 코드 와 같 습 니 다.
public static LogUtil getInstance() {
	if (logUtilInstance == null) {
		logUtilInstance = new LogUtil();
	}

	return logUtilInstance;
}

현재 getInstance 방법 을 동시에 실행 하 는 두 개의 스 레 드 가 있다 면 첫 번 째 스 레 드 가 두 번 째 줄 을 막 실 행 했 고 세 번 째 줄 을 실행 하지 않 았 습 니 다. 이때 두 번 째 스 레 드 가 두 번 째 줄 로 실 행 했 습 니 다. sLogUtil 인지 null 인지 알 수 있 기 때문에 if 판단 에 들 어 갔 습 니 다. 이렇게 하면 하나의 스 레 드 모드 는 실 패 했 습 니 다. 두 개의 다른 인 스 턴 스 를 만 들 었 기 때 문 입 니 다.
당신 은 문득 크게 깨 달 았 습 니 다. 그러나 당신 의 생각 은 매우 빠 릅 니 다. 바로 해결 방법 이 생각 났 습 니 다. 방법 에 동기 화 자 물 쇠 를 추가 하면 됩 니 다. 코드 는 다음 과 같 습 니 다.
public synchronized static LogUtil getInstance() {
	if (logUtilInstance == null) {
		logUtilInstance = new LogUtil();
	}

	return logUtilInstance;
}

이렇게 하면 같은 시간 에 하나의 스 레 드 만 getInstance 에 있 는 코드 를 실행 할 수 있 습 니 다. 그러면 위 에서 두 개의 인 스 턴 스 를 만 드 는 상황 을 효과적으로 해결 할 수 있 습 니 다.
당신 의 leader 는 당신 의 새 코드 를 보고 말 했다. "네, 좋 습 니 다. 이것 은 두 개의 인 스 턴 스 를 만 들 수 있 는 상황 을 확실히 해결 하 였 지만, 이 코드 는 여전히 문제 가 있 습 니 다."
너 는 긴장 하기 시 작 했 는데, 어떻게 문제 가 있 을 수 있 니?
당신 의 leader 웃음:"긴장 하지 마 세 요. 이번 에는 bug 가 아니 라 성능 적 으로 최적화 할 수 있 습 니 다. 보 세 요. getInstance 방법 에 synchronized 를 추가 하면 저 는 getInstace 방법 을 실행 할 때마다 동기 화 자물쇠 의 영향 을 받 습 니 다. 이렇게 하면 운행 효율 이 떨 어 집 니 다. 사실은 LogUtil 인 스 턴 스 를 처음 만 들 때 동기 화 자 물 쇠 를 추가 하면 됩 니 다. 제 가 할 게 요.""그것 을 어떻게 최적화 하 는 지 가르쳐 드릴 게 요."
우선 synchronized 키 워드 를 방법 설명 에서 제거 하고 방법 체 에 추가 합 니 다.
public static LogUtil getInstance() {
	if (logUtilInstance == null) {
		synchronized (LogUtil.class) {
			if (logUtilInstance == null) {
				//       ,              synchronized  
				logUtilInstance = new LogUtil();
			}
		}
	}

	return logUtilInstance;
}

코드 가 이렇게 바 뀌 면 sLogUtil 이 초기 화 되 지 않 았 을 때 만 세 번 째 줄 에 들 어가 고 동기 화 자 물 쇠 를 추가 합 니 다. sLogUtil 이 초기 화 되 었 을 때 더 이상 세 번 째 줄 에 들 어가 지 못 합 니 다. 그러면 getInstance 방법 을 실행 해도 동기 화 자물쇠 의 영향 을 받 지 않 고 효율 이 어느 정도 향상 되 었 습 니 다.
너 는 이 방법 이 정말 교묘 하 다 는 것 을 감탄 하지 않 을 수 없다. 정말 똑똑 하 다 는 것 을 생각 할 수 있다.
당신 의 leader 는 즉시 겸손 하기 시 작 했 습 니 다. "이런 방법 은 이중 잠 금 (Double - Check Locking) 이 라 고 합 니 다. 제 가 생각 한 것 이 아 닙 니 다. 더 많은 자 료 는 인터넷 에서 찾 아 보 세 요."
자기
사실 이 글 은 이미 매우 통속 적 이 고 이해 하기 쉬 워 졌 지만, 전재 임 에 도 불구 하고 자신의 총 결 이 없 으 면 블 로그 같 지 않 기 때문에 여기에 보충 해 야 한다.
위의 글 쓴 이 가 말 한 것 은 단일 모델 중의 게으름뱅이 모델 인 데, 사실 자바 에서 단일 사례 를 실현 하 는 것 은 나 는 굶 주 린 남자 모델 을 사용 하 는 것 에 더욱 익숙 하 다.
게으름뱅이 식 의 특징 은 로드 지연 입 니 다. 인 스 턴 스 는 사용 할 때 까지 불 러 옵 니 다.
굶 주 린 한식 의 특징 은 처음부터 불 러 오기 때문에 매번 사용 할 때 바로 돌아 오 면 된다 는 것 이다. (저 는 이런 것 을 더욱 추천 합 니 다. 스 레 드 안전 문 제 를 너무 많이 고려 할 필요 가 없 기 때 문 입 니 다. 물론 게 으 른 한식 은 앞에서 말 한 이중 잠 금 을 통 해 동기 화 문 제 를 해결 할 수 있 습 니 다)
굶 주 린 사람 으로 로그 기록 을 실현 하 는 코드 는 다음 과 같다.
public class LogUtil {
	private static final LogUtil logUtilInstance = new LogUtil();

	public final int DEBUG = 0;

	public final int INFO = 1;

	public final int ERROR = 2;

	public final int NOTHING = 3;

	public int level = DEBUG;

	private LogUtil() {

	}

	public static LogUtil getInstance() {
		return logUtilInstance;
	}

	public void debug(String msg) {
		if (DEBUG >= level) {
			System.out.println(msg);
		}
	}

	public void info(String msg) {
		if (INFO >= level) {
			System.out.println(msg);
		}
	}

	public void error(String msg) {
		if (ERROR >= level) {
			System.out.println(msg);
		}
	}

	public static void main(String[] args) {
		logUtil.getInstance().debug("Hello World!");
	}
}

좋은 웹페이지 즐겨찾기