Android 디자인 모드 - 단일 모드

6965 단어

정의


어떤 종류가 하나의 실례만 있을 뿐만 아니라, 자체적으로 실례화하여 전체 시스템에 이 실례를 제공하도록 확보한다.

장면 적용


특정한 유형이 있고 단지 하나의 대상만 있는 장면을 확보하고 여러 대상이 너무 많은 자원을 소모하지 않거나 특정한 유형의 대상이 있어야 하고 하나만 있어야 한다.예를 들어 하나의 대상을 만드는 데 소모되는 자원이 너무 많기 때문에 IO와 데이터베이스 등 자원을 방문하려면 단례 모델을 고려해야 한다.

키 포인트

  • 구조 함수는 대외적으로 개방되지 않는다.
  • 정적 방법이나 매거진을 통해 단일 클래스의 대상을 되돌려줍니다.
  • 단일 클래스의 대상이 있고 단 하나, 특히 다선정 환경에서 확보한다.
  • 단일 클래스의 대상이 반서열화될 때 대상을 재구성하지 않도록 확보한다.

  • 단일 모드 구현 방식


    실현 방식은 비교적 많지만 모두 각자의 장단점이 있다.

    굶주린 사람 모드


    굶주린 자 모드는 클래스가 불러올 때 하나의 클래스 실례를 만드는 것입니다.
    이루어지다
    public class HungerSingleton {
        private static HungerSingleton mInstance = new HungerSingleton();
    
        private HungerSingleton() {
        }
    
        public static HungerSingleton getInstance() {
            return mInstance;
        }
    }
    

    instance는 정적 대상입니다. 성명할 때 초기화되어 대상의 유일성을 보장합니다.
    이점:
  • 간단명료하여 라인 동기화에 신경 쓸 필요가 없다.
  • 대상을 비교적 빨리 얻기 때문에 다른 어떤 일도 할 필요가 없다.

  • 단점:
  • 클래스를 불러올 때 초기화 대상이 필요하기 때문에 느립니다.
  • 쓸데없는 대상이 많이 생길 수 있다.

  • 적용 장면
    만약 단일 모드 실례가 시스템에서 자주 사용된다면, 이 방식을 선택하여 단일 사례를 실현하는 것이 비교적 적합하다.그러나 대상 초기화 작업이 복잡하고 프로그램 실행 과정에서 반드시 이 단일 대상을 사용하지 않는다면 굶주린 사람 모드는 적합하지 않다.

    게으름뱅이 모드


    게으름뱅이 모드는 굶주린 사람 모드와 달리 getInstance () 를 처음 호출할 때 단일 대상을 초기화합니다.
    이루어지다
    public class LazySingleton {
        private static LazySingleton mInstance;
    
        private LazySingleton() {
        }
    
        public static synchronized LazySingleton getInstance() {
            if (mInstance == null) {
                mInstance = new LazySingleton();
            }
            return mInstance;
        }
    }
    

    getInstance () 에synchronized 키워드를 추가하면 두 개의 라인이 동시에 이 방법에 접근하지 않고 중복 대상을 만들 수 있습니다. getInstance () 를 동기화 방법으로 바꾸면 단일 대상의 유일성을 보장합니다.그러나 초기화된 후에 대상을 얻을 때 동기화 비용을 초래하는 것은 불가피하다.
    장점
  • 자원을 절약하고 사용할 때 단례 대상을 초기화한다.

  • 결점
  • 첫 번째 호출 반응이 느리고 불러오는 시간이 필요합니다.
  • 매번 호출할 때마다 동기화를 진행하여 불필요한 동기화 비용을 초래한다.

  • DCL 모드


    DCL의 장점은 대상을 초기화할 수 있을 뿐만 아니라, 라인의 안전도 보장할 수 있으며, 초기화한 후에 getInstance () 를 호출하여 동기화 잠금을 하지 않는다는 것이다.
    이루어지다
    public class DCLSingleton {
        private static DCLSingleton mInstance = null;
    
        private DCLSingleton() {
        }
    
        public static DCLSingleton getInstance() {
            if (mInstance == null) {
                synchronized (DCLSingleton.class) {
                    if (mInstance == null) {
                        mInstance = new DCLSingleton();
                    }
                }
            }
            return mInstance;
        }
    }
    

    외부에서 instance에 대한 판정은 불필요한 동기화 작업을 피하고 대상을 초기화할 때만 동기화가 필요합니다.
    내부 인스턴트의 판정은 대상의 유일함을 보증한다.
    DCL 실효 문제
    이런 방법은 당분간은 안전하지 않아서 일정한 일례가 있을 수도 있고 실패할 수도 있다.주요 원인은 new의 한 대상이 원자성이 아니라 세 개의 어셈블리 명령으로 컴파일되어 세 단계로 나뉘기 때문이다.그러나 JVM의 명령이 다시 정렬되기 때문에 이 세 단계의 다음 두 단계의 순서는 정해지지 않는다.
  • 메모리 공간을 할당합니다.
  • 메모리 공간을 초기화합니다.
  • 인용 변수는 이 메모리 공간을 가리킨다.

  • 명령 순서재정리는 JVM이 명령을 최적화하고 프로그램 실행 효율을 높이기 위한 것입니다.명령 재배열은 컴파일러 재배열과 실행 시 재배열을 포함한다.JVM 사양에 따르면 명령 순서재정리는 단일 스레드 프로그램 실행 결과에 영향을 주지 않는 전제에서 수행할 수 있습니다.
    지령 재배열은 다선정의 상황을 고려하지 않았기 때문에 아래의 장면이 나타날 수 있다.
    Thread 1
    Thread 2
    바깥쪽 instance 공백 판정: yes
    -
    동기화 코드 블록 진입
    -
    내부 instance 공백 판정: yes
    -
    단일 개체에 메모리 공간 할당
    -
    instance가 이 메모리 공간을 가리킨다
    -
    -
    외부 instance 공백 판정: no
    -
    초기화되지 않은 instance 대상을 직접 되돌려줍니다
    이 메모리 공간 초기화
    -
    이렇게 하면 초기화되지 않은 대상을 얻을 수 있다. 이것이 바로 DCL의 실효 문제이다.
    실효 문제를 해결하다
    자바5 이후volatile 키워드를 구체화하였기 때문에 자바5 이후에는 이 키워드로 DCL의 실효 문제를 해결할 수 있습니다.
    //  volatile  instance
    private volatile static DCLSingleton mInstance = null;
    

    volatile는 명령의 재배열을 방지합니다. new 프로세스의 명령과 변수에 인용된 값을 부여하는 문장의 재배열을 금지합니다. 부여는 new가 끝난 후에만 발생합니다.이렇게 하면 상술한 실효 문제가 나타나지 않을 것이다.
    volatile은 성능에 영향을 미치지만 DCL의 정확성을 보장합니다.
    장점
  • 자원을 절약하고 사용할 때 단례 대상을 초기화한다.

  • 결점
  • 첫 번째 호출 반응이 느리고 불러오는 시간이 필요합니다.
  • 일정한 확률로 문제가 발생할 수 있다.jdk5 이후volatile를 사용하여 정확성을 확보할 수 있으며,volatile는 많거나 적거나 성능에 영향을 줄 수 있습니다.

  • 정적 내부 클래스


    또한 getInstance()를 처음 호출해야 단일 객체를 초기화할 수 있습니다.
    이루어지다
    getInstance () 를 처음 호출할 때 가상 기기에singletonHolder 클래스를 불러와 단일 대상을 초기화할 수 있습니다. 이런 방법은 라인 안전을 보장할 뿐만 아니라 단일 대상의 유일성을 보장할 뿐만 아니라 단일 대상의 실례화도 지연시켜 실현하기가 매우 간단합니다.
    public class StaticInnerSingleton {
        private StaticInnerSingleton() {
        }
    
        public static StaticInnerSingleton getInstance() {
            return SingletonHolder.mInstance;
        }
    
        private static class SingletonHolder {
            private static final StaticInnerSingleton mInstance = new StaticInnerSingleton();
        }
    }
    

    클래스를 초기화하는 동안 JVM은 자물쇠를 가져옵니다. 이 자물쇠는 여러 개의 라인이 같은 클래스에 대한 초기화를 동기화하여 하나의 예를 유일하게 보장합니다.

    매거


    이것은 아마도 가장 간단한 단례로 실현되었을 것이다.매거는 일반 클래스와 마찬가지로 필드와 방법을 가질 수 있으며, 기본 매거 실례 창설은 라인이 안전하며, 여전히 시간대에 하나의 예이며, 반서열화를 지원하더라도 새로운 단일 대상을 생성하지 않습니다.반서열화는 일반 클래스와 다르며, 매거는 이름에 따라 존재하는 대상을 찾는 것이지, 다시 만드는 대상이 아니다.
    이루어지다
    public enum EnumSingleton {
        INSTANCE;
        public int a = 2;
    
        public int aaa() {
            return a;
        }
    }
    

    매거진이 서열화될 때 자바는 매거 대상의name 속성만 결과에 출력하고 반서열화될 때 통***가 자바를 통과합니다.lang.Enum의valueOf 방법으로 이름에 따라 열거 대상을 찾습니다.
    역서열화 지원
    위의 몇 가지 방법은 완전한 단례를 확보하려면readResolve()를 늘려야 하기 때문에 다음과 같은 수정을 해야 한다. 서열화된 굶주린 사람을 지원하는 모델을 예로 들자.
    public class HungerSingleton implements Serializable {
        private static final long serialVersionUID = 1L;
        //...
        private Object readResolve() throws ObjectStreamException {
            return mInstance;
        }
    
    

    새 대상을 다시 만드는 것이 아니라 단례 대상을 직접 되돌려줍니다.

    집합 실현


    이루어지다
    하나의 맵을 통해 모든 단례 대상을 저장합니다. 이런 방식은 단례 자체에서 단례 저장 방식으로 이동하고 get (),register () 를 제공하여 단례를 제공하고 단례를 가져옵니다.
    public class MapSingletonManager {
        private static Map objMap = new HashMap<>();
    
        private MapSingletonManager() {
        }
    
        public static void registerService(String key, Object instance) {
            if (!objMap.containsKey(key)) {
                objMap.put(key, instance);
            }
        }
    
        public static Object getService(String key) {
            return objMap.get(key);
        }
    }
    

    이런 방법은 결합을 낮추어 이전의 방법처럼 단례 논리를 분류에 섞지 않는다.그러나 이런 방식은 스스로 하나의 단일 대상을 만들어야 하고 무더기에 하나의 단일 대상만 있다는 것을 보장할 수 없으며 사용할 때 맵에서 추출한 것이 같은 단일 대상이라는 것만 보장할 수 있다.

    소결


    책에서 DCL과 정적 내부류의 방식으로 단례를 실현하는 것을 추천하고 마지막으로 단례 모델의 장단점을 정리한다.

    장점

  • 메모리 오버헤드 감소
  • 시스템 성능 오버헤드 감소
  • 리소스에 대한 멀티태스킹을 방지합니다.
  • 는 전역 접근점을 설정하고 자원 접근을 최적화하고 공유할 수 있다.

  • 결점

  • 확장이 어려워 인터페이스가 없어요.
  • 단일 대상이 Context를 가지고 있으면 메모리 유출이 발생하기 쉽다.

  • 만약에 아무렇게나Activity의Context를 전송한다면 이Activity가 필요하든 안 필요하든 간에 Context는 단일 대상이 가지고 있기 때문에Activity는 회수할 수 없고 항목만 살아있다면 이Activity는 살아있기 때문에 메모리 유출을 초래한다.따라서 단례에 Context가 필요하다면 Application의 Context를 전달하는 것이 좋다. 왜냐하면 Application의 생명주기는 응용과 마찬가지로 길기 때문이다.

    좋은 웹페이지 즐겨찾기