Android 개발-ExecutorService 를 통 해 앱 이 사용 하 는 전역 스 레 드 풀 구축

스 레 드 탱크
프롤로그
  • ThreadPoolExecutor
  • Executors

  • 구조 방법
    스 레 드 탱크 운행 논리
    사용자 정의 스 레 드 탱크스 레 드 탱크 닫 기
    머리말
    안 드 로 이 드 개발 에서 스 레 드 의 사용 은 틀림없이 없어 서 는 안 될 것 이다.왜냐하면 메 인 스 레 드 에 서 는 시간 을 소모 하 는 작업 을 할 수 없 기 때문이다.그러나 스 레 드 를 사용 해도 new Thrad 방식 으로 직접 사용 할 수 없습니다.목록 과 같은 페이지 는 불 러 와 야 할 그림 이 많 기 때문에 비동기 로 만 실행 할 수 있 습 니 다.new 를 직접 사용 하면 핸드폰 메모리 에 대한 압력 을 짐작 할 수 있 습 니 다.다행히 안 드 로 이 드 는 스 레 드 탱크 와 같은 것 을 제공 하여 스 레 드 를 관리 하 는 작업 을 제공 합 니 다.그런데 Android 는 우리 에 게 AsyncTask 와 같은 종 류 를 제공 해 주 었 습 니 다.내부 에 스 레 드 탱크 를 유지 하고 있 습 니 다.그러나 매우 아 픈 것 은 이 스 레 드 탱크 가 직렬 로 실행 되 고 병행 작업 을 할 수 없습니다.그러면 우 리 는 스스로 포장 할 수 밖 에 없습니다.
    본 논문 에 포 함 된 코드 는 수시로 업데이트 되 며,여기에서 최신 코드 전송 문 을 다운로드 할 수 있다.
    ThreadPoolExecutor
    Android 에서 스 레 드 에 관 한 API 는 자바 의 조상 이 Executor 라 는 인터페이스 로 스 레 드 를 실행 하 는 방법 execute(Runnable command)를 제공 합 니 다.또 다른 인터페이스 Executor Service 는 Executor 를 계승 하여 능력 을 확장 시 켰 다.
    항상 인 터 페 이 스 를 할 수 는 없 잖 아 요.누 군가 이런 개 세 무공 을 실현 해 야 죠.맞아요.바로 Thread PoolExecutor 라 는 손자 예요.
    Executors
    자바 가 Executors 류 를 제공 한 다 는 것 을 알 아야 한다.이 를 통 해 다양한 스 레 드 풀 을 구축 할 수 있다.
  • ExecutorService new CachedThreadPool():캐 시 가능 한 스 레 드 풀 을 만 듭 니 다.그 중에서 핵심 스 레 드 수량 은 0 이 고 최대 스 레 드 수량 은 Integer.MAX 입 니 다.VALUE(2 의 31 차방 감 1),스 레 드 대기 열 은 SynchronousQueue 이 며,시간 초과 시간 은 60s 이 며,스 레 드 의 남 은 시간 이 60s 에 이 르 면 회 수 됩 니 다.스 레 드 를 제출 할 때마다 스 레 드 탱크 에 빈 스 레 드 가 있 으 면 새 작업 을 수행 합 니 다.없 으 면 스 레 드 를 새로 만 들 고 작업 을 수행 합 니 다
  • ExecutorService new Fixed ThreadPool(int nThreads):지정 한 용량 의 스 레 드 풀 을 만 듭 니 다.그 중에서 핵심 스 레 드 수량 은 최대 스 레 드 수량 과 같 습 니 다.스 레 드 대기 열 은 링크 드 BlockingQueue 이 고 대기 열 용량 은 Integer.MAX 입 니 다.VALUE,시간 초과 가 0 s 인 것 은 스 레 드 가 비어 있어 도 회수 되 지 않 는 다 는 것 을 의미 합 니 다.그러면 스 레 드 탱크 는 요청 에 더욱 빨리 응답 할 수 있 습 니 다.그 다음 에 핵심 스 레 드 가 모두 임 무 를 수행 하고 있 으 면 나머지 임 무 는 핵심 스 레 드 가 남 을 때 까지 기다 릴 수 밖 에 없다
  • Executor Service new Single ThreadExecutor():하나의 스 레 드 만 있 는 스 레 드 풀 을 만 듭 니 다.그 중에서 핵심 스 레 드 수량 과 최대 스 레 드 수량 은 모두 1 이 고 스 레 드 대기 열 은 링크 드 BlockingQueue 이 며 대기 열 용량 은 Integer.MAX 입 니 다.VALUE,시간 초과 0;이런 스 레 드 는 영원히 하나의 스 레 드 만 일 하고 있다.
  • ScheduledExecutorService new ScheduledThreadPool(int corePoolSize):정기 적 으로 작업 을 수행 하 는 기능 을 가 진 스 레 드 풀 을 만 듭 니 다.핵심 스 레 드 수 는 우리 가 지정 합 니 다.최대 스 레 드 수 는 Integer.MAX 입 니 다.VALUE,시간 초과 10ms,스 레 드 대기 열 DelayedWorkQueue;인터페이스 클래스 Scheduled Executor Service 로 되 돌아 가 는 것 을 볼 수 있 습 니 다.구현 클래스 는 Scheduled Thread PoolExecutor(Thread PoolExecutor 계승)입 니 다.시작 지연 작업 과 정시 작업 수행 방법 을 제공 합 니 다
  • 이런 화려 한 방법 을 보지 마라.사실 그들 내 부 는 모두 다음 과 같은 유사 한 모델 을 통 해 스 레 드 풀 을 만 드 는 것 이다.
    new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue());
    

    이제 알 겠 습 니 다.Executors 류 는 공식 적 으로 개발 자 들 의 편 의 를 위해 스 레 드 탱크 를 봉인 한 것 입 니 다.그래서 자신의 APP 에 맞 는 스 레 드 탱크 를 만 들 려 면 스스로 스 레 드 탱크 를 밀봉 하 세 요.(사실 시간 지연 작업 은 ThreadPoolExecutor+Handler 를 통 해 할 수 있 습 니 다)
    구조 방법
    Thread PoolExecutor 는 네 개의 무 거 운 구조 방법 을 제공 합 니 다.
  • ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable> workQueue)
  • ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable> workQueue,RejectedExecutionHandler handler)
  • ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable> workQueue,ThreadFactory threadFactory)
  • ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

  • 여기 서 매개 변수 가 가장 많은 구조 방법 을 예 로 들 어 안의 매개 변 수 를 설명 한다.
  • core PoolSize:스 레 드 탱크 의 핵심 스 레 드 수량;메모리 최적화 를 위해 온라인 스 레 드 탱크 는 몇 가지 중요 한 스 레 드 를 유지 하고 일정한 조건 에 이 르 지 못 하면 나머지 스 레 드 를 열지 않 습 니 다
  • maximum PoolSize:스 레 드 탱크 의 최대 스 레 드 수량:이 수량 은 핵심 스 레 드 를 포함 합 니 다.스 레 드 탱크 에서 실행 중인 스 레 드 탱크 가 이 숫자 에 이 르 렀 을 때 스 레 드 를 제출 합 니 다.특수 처 리 를 하지 않 으 면 이상 을 던 집 니 다
  • keepAliveTime:비 핵심 스 레 드 의 시간 초과;스 레 드 탱크 의 비 핵심 스 레 드 방치 시간 이 이 값 을 대표 하 는 시간 을 초과 하면 회수 합 니 다.또한 ThreadPoolExecutor.allowCoreThreadTimeOut(true)을 호출 하면 핵심 스 레 드 도 이 설정 에 부합 합 니 다
  • unit:keepAlive Time 값 의 단 위 는 시 분 초 등 이 될 수 있 습 니 다
  • work Queue:실행 중인 스 레 드 를 저장 합 니 다.execute 방법 을 통 해 스 레 드 를 제출 하지만 이 스 레 드 가 실행 조건 에 이 르 지 않 으 면 이 대기 열 에 저 장 됩 니 다
  • thread Factory:스 레 드 탱크 를 만 드 는 공장;이 공장 에서 우 리 는 라인 의 일부 정 보 를 지정 할 수 있다.
  • handler:스 레 드 제출 거부 정책;보통 스 레 드 탱크 에서 실행 중인 스 레 드 수량 이 최대 스 레 드 수 에 이 르 렀 습 니 다.전송 하지 않 으 면 기본적으로 Rejected Execution Exception 을 던 지 므 로 전달 하 는 것 이 좋 습 니 다
  • 스 레 드 탱크 운행 논리
    구조 방법 을 알 게 된 후,하나의 스 레 드 탱크 의 통용 운행 논 리 는 어떤 것 입 니까?즉,스 레 드 탱크 의 스 레 드 실행 논 리 는 무엇 입 니까?
    우리 가 execute 방법 을 통 해 스 레 드 를 제출 한 후에 그 행방 은 다음 과 같다.(여기 서 핵심 스 레 드 수가 0 인 경우 제외)
    4.567917.스 레 드 탱크 에서 실행 중인 스 레 드 수량 이 핵심 스 레 드 수량 을 초과 하지 않 았 을 때 바로 핵심 스 레 드 를 새로 만들어 임 무 를 수행 합 니 다
  • 스 레 드 탱크 에서 실행 중인 스 레 드 수량 이 핵심 스 레 드 수량 에 달 했 지만 스 레 드 큐 가 가득 하지 않 으 면 제출 한 작업 은 대기 열 에 저 장 됩 니 다
  • 스 레 드 탱크 에서 실행 중인 스 레 드 수량 이 핵심 스 레 드 수량 에 달 했 고 스 레 드 대기 열 워 크 큐 가 가득 찼 습 니 다.이때 비 핵심 스 레 드 를 새로 만 들 고 임 무 를 수행 합 니 다
  • 스 레 드 탱크 에서 실행 중인 스 레 드 수량 이 최대 스 레 드 수량 에 달 했 고 스 레 드 대기 열 워 크 큐 가 가득 찼 을 때 작업 을 제출 하면 실행 을 거부 합 니 다.개발 자가 처리 하지 않 으 면 이상 을 던 집 니 다
  • 사용자 정의 스 레 드 탱크
    위의 이러한 지식 포 인 트 를 알 게 된 후에 우 리 는 우리 자신의 APP 수요 에 부합 되 는 스 레 드 풀 을 만 들 수 있다.
    먼저 해 야 할 일 은 스 레 드 탱크 의 핵심 스 레 드 수량 과 최대 스 레 드 수량 을 확인 하 는 것 이다.
    4.567917.핵심 스 레 드 수량:사실은 위의 스 레 드 탱크 운행 논 리 를 통 해 알 수 있 듯 이 스 레 드 탱크 에서 실행 중인 스 레 드 수량 이 핵심 스 레 드 수량 에 달 했 지만 work Queue 대기 열 은 0 에서 가득 차 있 고 핵심 스 레 드 가 실행 되 지 않 은 이 시간 동안 작업 을 수행 할 수 없습니다.모두 대기 열 에 저장 되 어 있 기 때문에 이것 은 매우 중요 합 니 다.원 하 는 병발 효 과 는 사실상 이 루 지 못 했다.그러나 핵심 스 레 드 수량 을 0 으로 설정 할 수 없습니다.그러면 대기 열 이 가득 차 야 비 핵심 스 레 드 를 열 어 임 무 를 수행 할 수 있 습 니 다.만약 당신 의 대기 열 길이 가 매우 크다 면 당신 을 기다 리 는 것 은 OOM 입 니 다.그래서 이 대열 의 길 이 는 반드시 설정 해 야 합 니 다4.567917.최대 스 레 드 수량:이 값 은 앱 에 얼마나 많은 병발 량 이 있 는 지 고려 해 야 합 니 다.예 를 들 어 한 화면 에 몇 개의 목록 을 표시 할 수 있 는 지또 하 나 는 handler 에 들 어가 스 레 드 탱크 가 거부 하 는 상황 에 대응 해 야 합 니 다.그러면 우 리 는 스 레 드 탱크 를 정의 할 수 있 습 니 다.다음 과 같 습 니 다.
    /**
     * @Description TODO(        )
     * @author cxy
     * @Date 2018/11/14 17:22
     */
    public class LocalThreadPools {
    
        private static String TAG = LocalThreadPools.class.getSimpleName();
    
        private static ExecutorService THREAD_POOL_EXECUTOR;
    
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT-1,4));
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2;
        private static final int KEEP_ALIVE_SECONDS = 60;
        private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue<>(8);
        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            public Thread newThread(Runnable r) {
                return new Thread(r, "MangoTask #" + mCount.getAndIncrement());
            }
        };
    
        private void initThreadPool() {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory,new RejectedHandler()){
                @Override
                public void execute(Runnable command) {
                    super.execute(command);
                    Log.e(TAG,"ActiveCount="+getActiveCount());
                    Log.e(TAG,"PoolSize="+getPoolSize());
                    Log.e(TAG,"Queue="+getQueue().size());
                }
            };
            //              
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
    
        private class RejectedHandler implements RejectedExecutionHandler {
    
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                //              
                Toast.makeText(mContext.get(),"         ,     ",Toast.LENGTH_SHORT).show();
            }
        }
    
        private WeakReference mContext;
        private static LocalThreadPools instance;
        private LocalThreadPools(Context context){
            mContext = new WeakReference<>(context);
            initThreadPool();
        }
        public static LocalThreadPools getInstance(Context context){
            if (instance == null) {
                instance = new LocalThreadPools(context);
            }
            return instance;
        }
    
        public void execute(Runnable command){
            THREAD_POOL_EXECUTOR.execute(command);
        }
    
    }
    

    호출 방법 은 다음 과 같다.
    LocalThreadPools.getInstance(this).execute(new Runnable() {
        @Override
        public void run() {
            Log.e(TAG,"NAME="+Thread.currentThread().getName());
        }
    });
    

    스 레 드 풀 닫 기
    스 레 드 풀 을 만 드 는 방법 이 있 으 니 닫 을 수 있 습 니 다.다음 과 같 습 니 다.
    /**
         *   interrupt             ,                
         *                
         *         
         * @return          
         */
        public List shutdownNow(){
            return THREAD_POOL_EXECUTOR.shutdownNow();
        }
    
        /**
         *             
         *         
         *             
         *                      
         */
        public void shutDown(){
            THREAD_POOL_EXECUTOR.shutdown();
            sPoolWorkQueue.clear();
        }
    

    좋은 웹페이지 즐겨찾기