EventBus 시리즈 "번 외" - "PendingPostQueue" 대열 의 실현 사상 을 진지 하 게 분석 합 니 다.

9747 단어
우 리 는 등록, 취소, post 와 post Sticky 사건 의 송 수신 을 모두 분 석 했 습 니 다. 다음은 EventBus 대열 의 실현 사상 을 말씀 드 리 겠 습 니 다. 제 다른 글 주소, 환영 품 읽 기:
EventBus 시리즈 "1" - 등록 및 로그아웃
EventBus 시리즈 "2" - Post 와 post Sticky 이벤트 의 발표 와 수신
EventBus 시리즈 "번 외" - "PendingPostQueue" 대열 의 실현 사상 을 진지 하 게 분석 합 니 다.
콘 셉 트
무엇이 대열 입 니까?
대기 열 은 FIFO, 꼬리 부분 추가, 머리 삭제 (선진 대기 열의 요소 가 먼저 대기 열 에서 나 오 는 것 을 지원 하 는 데이터 구조 입 니 다.
자바 의 상용 대기 열
자바 에서 BlockingQueue 인 터 페 이 스 를 제공 합 니 다. 이 인 터 페 이 스 를 실현 하여 대기 열 저장 을 실현 합 니 다. 우리 가 자주 사용 하 는 대기 열 은?
  • ArrayBlockingQueue 내부 에서 하나의 배열 을 유지 하고 배열 요소 에 대한 추가 삭제 와 검 사 를 통 해 대열 이 요소 FIFO (선진 선 출) 에 대한 순 서 를 실현 했다.
  • LinkedBlockingQueue 메모리 에서 노드 노드 노드 를 유지 하고 대기 열 에서 요소 FIFO (먼저 나 가기) 를 정렬 합 니 다.
  • 지식 흐름 깔개
    우 리 는 먼저 사건 을 대열 에 넣 는 과정 을 회상 해 보 자. threadMode = MAIN 을 예 로 들 자.
    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
            switch (subscription.subscriberMethod.threadMode) {
                ...
                case MAIN:
                    if (isMainThread) {
                        //      
                        invokeSubscriber(subscription, event);
                    } else {
                        //    
                        mainThreadPoster.enqueue(subscription, event);  
                    }
                    break;
             ...
    }
    

    진입 HandlerPoster.java, 실행 enqueue , 대기 열 에 Event 넣 기
    public void enqueue(Subscription subscription, Object event) {
           //   Event      PendingPost   
            PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
            synchronized (this) {
                //  
                queue.enqueue(pendingPost);
                if (!handlerActive) {
                    handlerActive = true;
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                }
            }
        }
    

    PendingPost Queue 대기 열의 실현PendingPostQueue 대열 의 실현 은 상기 두 대열 과 다르다. 그 내 부 는 유지 보수 도 없고 유지 보수 Node 도 없 으 며 유지 보수 List 도 없다. 그러면 어떻게 이 루어 졌 을 까? 답 은 메모리 포인터 이다.
    우 리 는 먼저 내부 유지 보수 의 변수 가 어떤 것 이 있 는 지 알 아 보 겠 습 니 다.
    final class PendingPostQueue {
        //  
        private PendingPost head; 
       //  
        private PendingPost tail;
    }
    

    소스 코드 를 통 해 우 리 는 PendingPostQueue 대열 의 머리 와 꼬리 부분 이 모두 PendingPost 유형 으로 포장 되 었 음 을 볼 수 있다. 우 리 는 PendingPost 류 의 실현 을 살 펴 보 자.
    final class PendingPost {
        private final static List pendingPostPool = new ArrayList();
    
        Object event;
        Subscription subscription;
        //    ,       PendingPost  
        PendingPost next; 
    
        private PendingPost(Object event, Subscription subscription) {
            this.event = event;
            this.subscription = subscription;
        }
        //@  :  Event        ,        。
        //    Event     PendingPost  
        static PendingPost obtainPendingPost(Subscription subscription, Object event) {
            synchronized (pendingPostPool) {
                int size = pendingPostPool.size();
                //  pendingPostPool    
                if (size > 0) {
                    //               
                    PendingPost pendingPost = pendingPostPool.remove(size - 1);
                    //           
                    pendingPost.event = event;
                    pendingPost.subscription = subscription;
                    pendingPost.next = null;
                    //         PendingPost   
                    return pendingPost;
                }
            }
           //new   PendingPost       
            return new PendingPost(event, subscription);
        }
     //  pendingPost       ,        10000
     static void releasePendingPost(PendingPost pendingPost) {
            pendingPost.event = null;
            pendingPost.subscription = null;
            pendingPost.next = null;
            synchronized (pendingPostPool) {
                // Don't let the pool grow indefinitely
                if (pendingPostPool.size() < 10000) {
                    pendingPostPool.add(pendingPost);
                }
            }
        }
    }
    

    우 리 는 지식 프로 세 스 를 깔 기 전에 전체 이벤트 이 벤트 를 PendingPost, 즉 실행 obtainPendingPost(Subscription subscription, Object event) 함수 로 봉 하 는 것 을 볼 수 있다.
    PendingPost Queue. java 로 돌아 가서 그 가 어떻게 창고 에 들 어가 고 창고 에서 나 오 는 지 봅 시다.
    메모리 포인터 로 다음 요소 의 메모리 위 치 를 가리 키 며 요소 값 을 가 져 오 는 방법 을 봅 시다.
    입고 절차
    final class PendingPostQueue {
        private PendingPost head;
        private PendingPost tail;
    
        synchronized void enqueue(PendingPost pendingPost) {
             //  pendingPost    
            if (pendingPost == null) {
                throw new NullPointerException("null cannot be enqueued");
            }
           //     ,            
            if (tail != null) {
                tail.next = pendingPost; // tail    next    
                tail = pendingPost;  //   tail    
            } else if (head == null) { //    ,       ,        
                head = tail = pendingPost; //        
            } else {
                throw new IllegalStateException("Head present, but no tail");
            }
            notifyAll();
        }
    }
    
  • 스 택 에 들 어 갈 때 Null 이 스 택 에 들 어가 지 않도록 먼저 Null 을 판결 합 니 다.
  • 스 택 에 처음으로 요소 A 를 삽입 하면 이때 head 와 tail 은 모두 Null 이 어야 합 니 다. 요소 A 를 삽입 하여 head 와 tail 에 동시에 부여 해 야 합 니 다. 이때 head 와 tail 의 메모리 포인 터 는 모두 A 를 가리 키 는 것 입 니 다
  • 3. 만약 에 우리 가 삽입 요소 B 를 다시 실행 하면 이때 head 와 tail 은 모두 가치 가 있 습 니 다. 그러면 우 리 는 tail.next = B 요소 B 를 A A.next 속성 치 에 부여 한 것 과 같 습 니 다. 이때 head.next 의 값 도 B 와 같 습 니 다. 이 어 우리 가 tail 에 새로운 값 B 를 부여 하면 이때 A.nexthead.next 는 tail 과 같 습 니 다.
  • 4. 우 리 는 이렇게 데 이 터 를 계속 삽입 합 니 다. tail 마지막 값 을 영원히 저장 합 니 다. head.next 계속 옮 겨 다 니 면 스 택 에 있 는 모든 값 을 저장 할 수 있 습 니 다. FIFO (선진 선 출) 정렬 원칙
  • 을 실현 합 니 다.
    출고 절차
    final class PendingPostQueue {
        private PendingPost head;
        private PendingPost tail;
         //  
        synchronized PendingPost poll() {
            PendingPost pendingPost = head; //      
            if (head != null) {  // head   ,          
                head = head.next; //         ,   head
                if (head == null) { //          ,        
                    tail = null; //      Null
                }
            }
            return pendingPost; //  pendingPost  
        }
        //  ,        
        synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
            if (head == null) {
                wait(maxMillisToWait); //    
            }
            return poll();
        }
    
    }
    

    첫 번 째 실행 poll 은 머리의 pendingPost 요 소 를 가 져 온 다음 에 head 값 이 비어 있 는 지 판단 합 니 다. Null 이 아니라면 head.next 값 을 다음 실행 poll head 값 으로 가 져 옵 니 다. 한 번 에 유추 head.nextNull 일 때 까지 다음 요소 가 없다 는 것 을 설명 하고 스 택 밑 에 있 습 니 다. tailNull 설치 하고 끝 냅 니 다.
    단순 실례
    편리 하 게 이해 하기 위해 저 는 EventBus 의 구조 에 따라 데모 샘플 을 만 들 었 습 니 다. 여러분 께 참고 하 시기 바 랍 니 다.
    Entry.java
    public class Entry {
        private String flag;
        Entry next;
    
        public Entry(String flag) {
            this.flag = flag;
        }
    
        public static Entry createEntry(String flag) {
            return new Entry(flag);
        }
    }
    

    EntryControl.java
    public class EntryControl {
        private Entry head;
        private Entry tail;
    
        synchronized void enqueue(Entry entry) {
            if (tail != null) {
                tail.next = entry;
                tail = entry;
            } else if (head == null) {
                head = tail = entry;
            } else {
                throw new IllegalStateException("Head present, but no tail");
            }
            System.out.println("Head HashCode:" + head.hashCode());
            System.out.println("Tail HashCode:" + tail.hashCode());
            notifyAll();
        }
    
        synchronized Entry poll() {
            Entry entry = head;
            if (head != null) {
                head = head.next;
                if (head == null) {
                    tail = null;
                }
                System.out.println("Current Head HashCode:" + entry.hashCode());
            }
            return entry;
        }
    }
    

    Test. java 실행 프로그램
    public class Test {
        public static void main(String[] args) {
            EntryControl entryControl = new EntryControl();
    
            for (int index = 0; index < 5; index++) {
                Entry entry = Entry.createEntry("   " + index);
                entryControl.enqueue(entry);
            }
            System.out.println("    ===========================");
            while (true) {
                Entry pendingPost = entryControl.poll();
                if (pendingPost == null) {
                    return;
                }
    //            System.out.println().e(TAG,"      " + pendingPost.subscription + "," + pendingPost.event);
            }
    
        }
    }
    

    실행 결과
    Head HashCode:356573597
    Tail HashCode:356573597
    Head HashCode:356573597
    Tail HashCode:1735600054
    Head HashCode:356573597
    Tail HashCode:21685669
    Head HashCode:356573597
    Tail HashCode:2133927002
    Head HashCode:356573597
    Tail HashCode:1836019240
        ===========================
    Current Head HashCode:356573597
    Current Head HashCode:1735600054
    Current Head HashCode:21685669
    Current Head HashCode:2133927002
    Current Head HashCode:1836019240
    

    이렇게 하면 분명 해진 다.
    This ALL! Thanks EveryBody!

    좋은 웹페이지 즐겨찾기