English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이 문서는 Android 프로그래밍 디자인 패턴 중 singleton 패턴에 대해 설명합니다. 여러분과 공유하고, 다음과 같이 자세히 설명합니다:
1. 소개
singleton 패턴은 가장 널리 사용되는 패턴 중 하나로, 초보자 엔지니어가 사용하는 유일한 디자인 패턴일 수도 있습니다. 이 패턴을 사용할 때, singleton 객체의 클래스는 하나의 인스턴스만 존재해야 합니다. 많은 경우 전체 시스템이 하나의 전체 객체를 필요로 하며, 이는 시스템 전체의 행동을 조율하는 데 유리합니다.
2. 정의
특정 클래스가 하나의 인스턴스만 가지고 있어야 하며, 자신이 인스턴스를 생성하고 전체 시스템에 이 인스턴스를 제공해야 합니다.
3. 사용 사례
특정 클래스가 하나의 객체만 가지고 있어야 하는 경우, 여러 개의 객체가 생성되어 많은 자원을 소모하거나, 특정 유형의 객체가 하나만 있어야 할 때를 피하기 위해 사용됩니다. 예를 들어, 객체를 생성할 때 많은 자원이 소모되는 경우(예: IO와 데이터베이스 접근 등) 이를 고려해야 합니다.
4. 구현 방법
1、어려운 홍콩식
예제 코드:
/** * 어려운 홍콩식 */ public class Singleton { private static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
장점: 지연 로드(필요할 때만 로드)
단점: multi-threading에서 동기화되지 않음, 여러 스레드에서 다른 동기화 상황이 쉽게 발생할 수 있습니다. 예를 들어, 데이터베이스 객체에서 반복적으로 읽고 쓰는 작업 시.
2、늦은 홍콩식
예제 코드:
/** * 늦은 홍콩식 */ public class Singleton { private static Singleton instance; private Singleton(){} public static synchronized Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
가벼운 싱글턴 패턴과 비교하여, getInstance() 메서드에 synchronized 키워드를 추가하여 getInstance는 동기화 메서드가 되었기 때문에, 다중 스레드 상태에서 싱글턴 객체의 유일성을 보장하는 수단이 됩니다. 그러나, 더 깊이 생각해 보면, instance가 초기화되었음에도 불구하고(최초 호출 때 instance가 초기화됨), 매번 getInstance 메서드를 호출할 때 동기화가 수행되어 불필요한 자원을 소모하게 되며, 이것이 라인하네스 모드가 가지고 있는 가장 큰 문제입니다.
장점:스레드 안전성 문제를 해결했습니다.
단점:처음 로드할 때는 즉시 인스턴스화를 수행해야 하며, 반응이 느리게 되고, 가장 큰 문제는 매번 getInstance을 호출할 때 동기화가 필요하여 불필요한 동기화 비용이 발생합니다.
보완:Android 소스 코드에서 사용하는 이 싱글턴 메서드는 InputMethodManager, AccessibilityManager 등이 이 패턴을 사용합니다.
3、Double Check Lock(DCL) 양중검사 락
예제 코드:
/** * Double Check Lock(DCL) 싱글턴 패턴 */ public class Singleton { private static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ synchronized (Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }
이 프로그램의 장점은 natural하게 getInstance 메서드에 있습니다. getInstance 메서드에서 instance에 대해 두 번의判空을 볼 수 있습니다. 첫 번째 층의 판단은 불필요한 동기화를 피하기 위함이며, 두 번째 층의 판단은 null이 되었을 때 인스턴스를 생성하기 위함입니다.
예를 들어, 스레드 A가 instance = new Singleton() 문장에 도달하면, 이는 한 줄의 코드처럼 보이지만 실제로는 원자적 작업이 아닙니다. 이 코드는 최종적으로 여러汇编 명령어로 컴파일되며, 대략 다음과 같은 작업을 수행합니다:3사항:
(1) 개의 Singleton 인스턴스에 메모리를 할당합니다;
(2) Singleton()의 생성자를 호출하여 멤버 필드를 초기화합니다;
(3이 instance 객체를 할당된 메모리 공간으로 가리키게 합니다(이제 instance는 null이 아니게 됩니다).
그러나, java 컴파일러가 프로세서의 혼란스러운 실행을 허용하고, JDK1.5JMM(Java Memory Model, 즉 Java 메모리 모델)에서 캐시, 레지스터에서 메인 메모리로 반영 순서가 정해져 있지 않기 때문에, 위의 두 번째와 세 번째 문장의 순서가 보장되지 않습니다. 즉, 실행 순서는 다음과 같을 수 있습니다.1-2-3가능할 수도 있습니다.1-3-2또는 후자라면, 그리고3끝나고、2이전에 실행되지 않은 상태에서, 스레드 B로 전환된 경우, 이때 instance는 스레드 A에서 이미 세 번째 점을 수행했기 때문에 instance는 비어 있지 않습니다. 따라서 스레드 B는 instance를 직접 가져갔고, 사용할 때 오류가 발생합니다. 이는 DCL 불성공 문제로, 추적하기 어렵고 재현하기 어려운 오류로서 오랫동안 숨겨질 수 있습니다.
JDK1.5그 이후로, SUN 공식이 이 문제를 고려하여 JVM을 조정하고, volatile 키워드를 명확히 했습니다. 따라서 JDK가1.5또는 이후 버전에서, instance 정의를 private volatile static Singleton instance로 변경하여 instance 객체가 항상 메인 메모리에서 읽히도록 보장할 수 있습니다. 따라서 DCL의 방식을 사용하여 싱글톤 패턴을 완료할 수 있습니다. 물론, volatile은 성능에多少 영향을 미칠 수 있지만, 프로그램의 정확성을 고려할 때 이러한 성능 상실은 충분히 귀중합니다.
장점:자원 활용률이 높으며, 처음에 getInstance을 실행할 때 싱글톤 객체가 인스턴스화되며, 효율성이 높습니다. 동기화량이 적고 보안성이 높지 않은 경우에는 싱글톤 패턴이 매우 완벽하게 작동할 수 있습니다.
단점:처음 로드할 때 반응이 느리거나, Java 메모리 모델의 이유로 어 때 실패할 수 있습니다. 고성능 환경에서도 일정한 결함이 있지만, 발생 확률은 매우 낮습니다.
보완:android 이미지 오픈 소스 프로젝트 Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)에서는 이 방식을 사용합니다.
DCL 패턴은 가장 많이 사용되는 싱글톤 구현 방식으로, 싱글톤 객체가 필요할 때만 인스턴스화하며, 대부분의 상황에서 싱글톤 객체의 유일성을 보장할 수 있습니다. 하지만, 코드가 복잡한 동기화 상황이나 JDK 이하의 경우를 제외하고는.6버전에서 사용하면, 그렇지 않으면 이 방식은 일반적으로 필요를 충족할 수 있습니다.
4정적 내부 클래스 싱글톤 패턴
DCL은 일정한 정도로 자원 소모, 불필요한 동기화, 스레드 안전성 등의 문제를 해결했지만, 여전히 일부 경우에서 불성공하는 문제가 있습니다. 이 문제는 '중복 확인 락(DCL) 불성공'으로 불리며, 《Java 컨currency Programming Practice》 책의 마지막 부분에서 이 문제에 대해 논의하고, 이러한 ' 최적화'는 추천하지 않는다고 언급했습니다. 대신 다음과 같은 코드를 대체하는 것을 권장합니다:
예제 코드:
/** * 정적 내부 클래스 싱글톤 패턴 */ public class Singleton { private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.instance; } /** * 정적 내부 클래스 * 불량 로드, 메모리 소모를 줄입니다 */ private static class SingletonHolder{}} private static final Singleton instance = new Singleton(); } }
Singleton 클래스를 처음 로드할 때 instance는 초기화되지 않으며, Singleton의 getInstance 메서드를 처음 호출할 때 instance가 초기화됩니다. 따라서, getInstance 메서드를 처음 호출할 때 SingletonHolder 클래스가 가상기계에 로드됩니다. 이 방식은 스레드 안전을 보장하고 싱글톤 객체의 유일성을 보장하며, 싱글톤의 인스턴스화를 지연시켜서 이는 추천하는 싱글톤 구현 방식입니다.
장점:대체로 지연 로드, 스레드 안전(Java에서 클래스 로드 시 mutual exclusive), 메모리 소비를 줄입니다
5열거형 싱글톤
앞에서는 싱글톤 패턴 구현 방법에 대해 설명했지만, 이러한 구현 방법은 조금은 복잡하거나 특정 경우에 문제가 발생할 수 있습니다.
예제 코드:
/** * 열거형 싱글톤 모델 */ public enum Singleton { /** * 1.Java에서1.5시작 지원; * 2.시리얼라이즈 메커니즘을 무료로 제공합니다; * 3.절대로 여러 번 인스턴스화를 방지하고, 복잡한 시리얼라이즈나 반영 공격에도 대처할 수 있습니다; */ instance; private String others; Singleton() { } public String getOthers() { return others; } public void setOthers(String others) { this.others = others; } }
코드 작성이 간단하다는 것은 싱글톤의 가장 큰 장점입니다. Java에서는 열거형과 일반 클래스가 같습니다. 필드를 가질 수 있으며, 자신의 메서드도 가질 수 있습니다. 가장 중요한 것은 기본 열거형 인스턴스 생성이 스레드 안전하며, 어떤 경우에도 싱글톤입니다.
이렇게 말하는 이유는 무엇인가요? 위의 몇 가지 싱글톤 패턴 구현에서는 특정 경우에 새로운 객체를 생성하는 경우가 있습니다. 그것은 시리얼라이즈입니다.
시리얼라이즈를 통해 하나의 싱글톤 인스턴스 객체를 디스크에 쓰고, 다시 읽어와서 효과적으로 인스턴스를 얻을 수 있습니다. 생성자가 프라이빗이어도, 시리얼라이즈 시 특별한 경로를 통해 클래스의 새로운 인스턴스를 생성할 수 있어, 해당 클래스의 생성자를 호출하는 것과 같습니다. 시리얼라이즈 작업은 특별한 힙킨스 함수를 제공하며, 클래스에는 인스턴스화된 프라이빗 메서드인 readResolve()가 있어 개발자가 객체의 시리얼라이즈를 제어할 수 있습니다. 예를 들어, 위의 몇 가지 예제에서 싱글톤 객체가 시리얼라이즈되면서 다시 객체가 생성되는 것을 방지하려면 다음과 같은 메서드를 추가해야 합니다:
private Object readResolve() throws ObjectStreamException { return instance; }
readResolve 메서드에서 instance 객체를 반환하는 것이 아니라 기본적으로 새로운 객체를 생성하는 것입니다. 반면에, 열거형은 이 문제가 없으며, 시리얼라이즈되면 새로운 인스턴스가 생성되지 않습니다.
장점기본적으로 시리얼라이즈 메커니즘을 무료로 제공하여, 반복적인 인스턴스화를 완전히 방지합니다. 복잡한 시리얼라이즈나 반영 공격에도 불구하고.
단점Java에서1.5지원을 시작했습니다.
위에서는 싱글턴 패턴에 대해 설명했습니다.5다양한 생성 방법이 있으며, 개인 프로젝트에서 사용할 때 장단점을 고려하여 선택할 수 있습니다.
Android와 관련된 더 많은 내용에 대해 관심이 있는 독자는 이 사이트의 특집을 확인할 수 있습니다:《Android 개발 입문 및 고급 교육》、《Android 디버깅 기술 및 일반 문제 해결 방법 요약》、《Android 기본 구성 요소 사용 요약》、《Android 뷰 View 기술 요약》、《Android 레이아웃 layout 기술 요약》 및 《Android 컨트롤러 사용 요약》
이 문서에서 설명한 내용이 모두 여러분의 Android 프로그램 설계에 도움이 되길 바랍니다.
고지사항: 이 문서의 내용은 인터넷에서 가져왔으며, 저작권자는 본인입니다. 인터넷 사용자가 자발적으로 기여하고 업로드한 내용이며, 이 사이트는 소유권을 가지지 않으며, 인공적인 편집을하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해 내용이 발견되면 notice#w 이메일로 신고하여 주세요.3codebox.com(댓글을 달 때는 #을 @으로 바꿔 주세요. 신고하고 관련 증거를 제공하시면, 사실 여부에 따라 이 사이트는 즉시 저작권 침해 내용을 삭제합니다。)