Android에서 단일 모드의 몇 개의 구덩이

7305 단어
먼저 이런 단례를 보면 약간의 경험이 있는 학우들은 이런 단례는 비선정이 안전하다고 말할 수 있다.volatile 키워드를 추가해야 합니다.
  class Singleton{
        private static  Singleton singleton;
        private Singleton(){};
        public static Singleton getInstance()
        {
            if (singleton==null)
            {
                synchronized (Singleton.class)
                {
                    if (singleton==null)
                    {
                        singleton=new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

그런데 왜 비선정이 안전한지 물어보면 답이 안 나와요.이 문제를 분명히 하는 것은 사실 우리의 다선정에 대한 이해에 매우 좋은 것이다.
우선 jvm에 대해 하나의 변수에 대한 쓰기 작업이 도대체 어떻게 진행되는지 명확히 하자.
쓰기 동작: (1) cpu의 고속 캐시 캐치에 값을 먼저 씁니다.(2) 그리고 이 캐치의 값을 람(즉 우리의 메모리)에 복사한다.
주의해라. 쓰기 작업에 있어서 이 (1) (2) 는 원자 작업이 아니다. (1) 실행이 끝난 후에 cpu가 다른 일을 했을 가능성이 높으며, 캐치의 값을 람에 가장 먼저 쓰지 않았을 수도 있다.우리가 읽는 동작은 모두 람에서 하나의 값을 읽는 것이다.
그래서 여기서 우리는 다선정 장면이라면 약간의 구덩이가 있을 것이라고 생각해 볼 수 있다.
그리고 또 하나의 개념을 말하면singleton=new Singleton().이 문장으로 말하자면, 그는 분명히 지령 하나로 완성할 수 있는 것이 아니다.
정상적인 상황에서 우리가 이 문장에 관련된 지령을 완성하려면 대략 다음과 같다.
1. 메모리 공간 추가 신청
2. 이 메모리 공간에서 우리가 필요로 하는 대상을 초기화합니다
3.singleton이라는 인용을 우리의 메모리 공간 주소를 가리킨다.
그러나 아버지를 괴롭히는 것은 아버지를 괴롭히는 것이다. 가상 기회는 지령을 재정렬하는 개념이 있다.가상 기기가 단일 라인에서 지령의 순서 변경이 결과 이상을 초래하지 않는다는 것을 발견할 때 지령의 순서 재배열 메커니즘을 촉발한다. 그는 상술한 123순서에 변경을 초래할 것이다. 예를 들어 우리가 순서를 132로 바꾸면 결과가 똑같다는 것을 발견할 수 있다.(지령 재배열의 촉발 메커니즘은 정확히 말하면 happens before 원칙에 관심이 있는 학생은 깊이 파헤칠 수 있다)
132의 실행 순서가 발생하면 어떻게 됩니까?
가령 라인 a가 동기화 코드 블록에 들어갔다고 가정하면 이때 지령을 터치하여 순서가 132가 되고 cpu가 이때 13을 실행했다고 가정한다.그리고 고개를 돌려 라인 b를 실행했다. 라인 b가 getInstance 방법에 들어갔을 때 그는singleton이null이 아니라는 것을 발견하고 기뻐했다. 그러나 이때 라인 a의 2가 아직 실행되지 않았다는 것을 알아야 한다. 즉, singleton은 비어 있지 않지만 그가 가리키는 주소 공간에는 아무것도 없고 대상은 아직 초기화되지 않았다는 것이다.그래서 이것은 매우 큰 위험이다. 비록 그가 발생할 확률이 매우 낮고 내가 지금까지 이런 현상을 재현한 적이 없지만 여전히 확률이 있다.
그렇다면 정확한 작법:
     class Singleton{
        private static volatile Singleton singleton;
        private Singleton(){};
        public static Singleton getInstance()
        {
            if (singleton==null)
            {
                synchronized (Singleton.class)
                {
                    if (singleton==null)
                    {
                        singleton=new Singleton();
                    }
                }
            }
            return singleton;
        }
    }


많은 사람들이volatile이라는 키워드를 말한 후에singleton=new Singleton().지령 재배열이 일어나지 않았을 테니 이렇게 하는 것이 옳다.
지금 분명히 말하지만, 위의 이 관점은 잘못된 것이다
singleton=new Singleton(); 이 문장 뒤의 지령은 여전히 확률적으로 지령 재배열이 발생한다. 단지volatile가 수식한 후에 이 문장 뒤의 지령이 완전히 집행되기 전에singleton이라는 인용에 대한 읽기 조작은 모두 차단되었다.
즉, 132의 실행 순서는 여전히 발생한다. 단지 13이 실행되고 2가 실행되지 않았을 때volatile가 수식한 이 변수는 그의 읽기 동작을 잠시 차단하고 2가 실행된 후에야 읽기 작업을 할 수 있다.
이것이야말로volatile 키워드를 더한 후의 작용이다.
android의 많은 코드, 예를 들어 이벤트bus의 단례는 상술한 문법을 사용한다.
물론 상술한 문법은 전형적인 게으름뱅이 문법이다. 게으름뱅이는 쓸 때 실례화하는 것으로 이해하고 쓰지 않으면 실례화되지 않는다.
그러나 만약 당신의 요구가 이 단례라면 어떤 상황에서도 존재할 것이다. 당신은 당연히 굶주린 사람으로 쓸 수 있고 굶주린 사람의 글씨는 더욱 간단하다.
단점은 그가 줄곧 메모리를 점용한다는 것이다.굶주린 자는 글씨를 많이 쓴다. 나는 가장 간단하게 쓴다.
   class Singleton {
        //           ,  public  
        public static final Singleton instance = new Singleton();

        private Singleton() {
        }

    }

단일 서열화는 대상의 유일성을 파괴합니까?
답은 다음과 같다.
package com.wuyue.test;

import java.io.*;

/**
 * Created by 16040657 on 2019/2/12.
 */
public class Test2 {


    public static void main(String args[]) {

        Singleton s1 = Singleton.instance;

        File f = new File("../test.txt");
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
            oos.writeObject(s1);
            oos.close();

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
            Singleton s3 = (Singleton) ois.readObject();

            System.out.println("s1==s3:" + (s1 == s3));

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }

    static class Singleton implements Serializable {
        //           ,  public  
        public static final Singleton instance = new Singleton();

        private Singleton() {
        }

//        //                           
//        private Object readResolve() {
//            return instance;
//        }

    }
}


코드가 비교적 간단합니다. 여러분이 테스트해 보십시오. s1과 s3은 두 개의 다른 대상입니다. 그러나 주석을 달아버린readResolve 방법을 놓으면 이 문제가 해결되었습니다. 서열화와 반서열화는 같은 대상입니다.
외부에서 공개적으로 제공하는 sdk의 단례에 대해 무엇을 주의해야 합니까?
특히 많은 금융 안전 유형의 sdk에 대해 말하자면 만약에 이 안에 단례가 있다면 안전성과 관련된 것은 가능한 한 업무측에 의해 훅되지 않도록 해야 한다. 그 중에서 특히 주의해야 할 것은 누군가가 반사를 이용하여 new의 대상을 파괴하고 단례를 파괴할 수 있다는 것이다.
이 문제를 해결하는 것도 어렵지 않지만,
 private Singleton() {
            //            
            if (null != instance) {
                throw new RuntimeException("dont construct more!");
            }

        }

프로젝트 중의 단례가 너무 많은데 어떻게 효과적으로 관리합니까?
사실 맵으로 관리하면 돼요.android 안에 있는wms,ams 등 시스템의 단일 서비스는 모두 이렇습니다.너는 키를 하나 보내서 너에게 단례를 되돌려 주어라.
이것은 정말 유용합니다. 특히 대형 공사는 단례를 효과적으로 관리할 수 있고 문서 출력은 훨씬 간단합니다.
    static class SingletonManager {
        private static Map objectMap = new HashMap<>();

        private SingletonManager() {
        }

        public static void registerService(String key, Object ins) {
            if (!objectMap.containsKey(key)) {
                objectMap.put(key, ins);
            }
        }

        public static Object getService(String key) {
            return objectMap.get(key);
        }

    }


android에서 단일 예시를 사용할 때 무엇을 주의해야 합니까?
가장 중요한 것은 일례 모드를 이용하여 전달 데이터를 저장하지 않는 것이다. 왜냐하면 앱이 백엔드에 걸렸을 때 프로세스가 죽기 쉽기 때문이다. 만약에 백엔드로 돌아가서 이 일례의 데이터를 다시 찾으면 null을 얻기 쉽기 때문에android에서 일례를 쓰는 원칙은 다음과 같다.
원칙적으로 일례 모델로 데이터를 전달하는 것을 허용하지 않기 때문에 반드시 이렇게 해야 한다면 데이터 복구 현장을 고려해 주십시오.

좋은 웹페이지 즐겨찾기