자바 스 레 드 탱크 와 다섯 가지 상용 스 레 드 탱크 정책 사용 및 분석

자바 스 레 드 탱크 와 다섯 가지 상용 스 레 드 탱크 정책 사용 및 분석
스 레 드 풀
왜 스 레 드 풀 을 사용 해 야 하 는 지 에 대해 서 는 군말 하지 않 습 니 다. 우선 자바 에서 스 레 드 풀 Executor 바 텀 실현 류 인 Thred PoolExecutor 의 구조 함 수 를 살 펴 보 겠 습 니 다.
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
       ...
    }

그 중에서 각 매개 변 수 는 다음 과 같다. core PoolSize - 풀 에 저 장 된 스 레 드 수 는 남 은 스 레 드 를 포함한다.주의해 야 할 것 은 스 레 드 풀 을 처음 만 들 때 스 레 드 가 바로 시작 되 지 않 습 니 다. 작업 이 제출 될 때 까지 스 레 드 를 시작 하고 점차적으로 스 레 드 수가 core PoolSize 에 달 합 니 다.처음부터 모든 핵심 스 레 드 를 만 들 려 면 prestart AllCoreThreads 방법 을 사용 해 야 합 니 다.maximum PoolSize - 풀 에서 허용 하 는 최대 스 레 드 수 입 니 다.주의해 야 할 것 은 핵심 스 레 드 가 가득 차고 차단 대기 열 이 가득 찼 을 때 현재 스 레 드 수가 최대 스 레 드 보다 적 는 지 판단 하고 새 스 레 드 를 만 들 지 여 부 를 결정 하 는 것 입 니 다.keepAliveTime - 스 레 드 수가 핵심 보다 많 을 때 남 은 스 레 드 의 최대 생존 시간 유닛 - keepAliveTime 매개 변수 보다 많은 시간 단위.workQueue - 스 레 드 수가 핵심 스 레 드 수 를 초과 할 때 작업 의 대기 열 을 저장 합 니 다.주로 3 가지 유형의 BlockingQueue 를 선택 할 수 있 습 니 다. 무한 대기 열, 경계 대기 열 과 동기 화 전환 이 있 습 니 다.아래 문장에서 상세 하 게 논술 할 것 이다.매개 변수 에서 볼 수 있 듯 이 이 대기 열 은 Runnable 인 터 페 이 스 를 실현 하 는 작업 만 저장 합 니 다.threadFactory - 실행 프로그램 이 새 스 레 드 를 만 들 때 사용 하 는 공장 입 니 다.handler - 차단 대기 열 이 가득 차 있 고 스 레 드 수가 최대 치 에 이 르 렀 을 때 사용 하 는 포화 정책 입 니 다.자바 기본 값 은 4 가지 포화 정책 의 실현 방식 을 제공 합 니 다. 중지, 포기, 버 리 기, 가장 오래된, 호출 자 실행 을 포기 합 니 다.아래 문장에서 상세 하 게 논술 할 것 이다.
2. 선택 가능 한 차단 대기 열 BlockingQueue 상세 설명
먼저 새 작업 이 들 어 갈 때 스 레 드 탱크 의 실행 전략 을 살 펴 보 겠 습 니 다. 만약 에 실행 하 는 스 레 드 가 corePoolSize 보다 적 으 면 Executor 는 항상 새로운 스 레 드 를 추가 하고 줄 을 서지 않 습 니 다.(현재 실행 중인 스 레 드 가 corePoolSize 보다 작 으 면 작업 은 quue 에 저장 되 지 않 고 직접 실행 합 니 다) 실행 중인 스 레 드 가 corePoolSize 와 같 으 면 Executor 는 항상 새로운 스 레 드 를 추가 하지 않 고 대기 열 에 가입 할 것 을 요청 합 니 다.대기 열 에 요청 할 수 없 으 면 새 스 레 드 를 만 듭 니 다. 이 스 레 드 가 maximumPoolSize 를 초과 하지 않 는 한 작업 이 거 부 됩 니 다.주로 세 가지 유형의 BlockingQueue 가 있 습 니 다.
2.1 무한 대열
대기 열 크기 는 제한 이 없습니다. 항상 사용 되 는 링크 드 BlockingQueue 입 니 다. 이 대기 열 을 차단 대기 열 로 사용 할 때 특히 조심해 야 합 니 다. 작업 시간 이 오래 걸 릴 때 대량의 새로운 작업 이 대기 열 에 쌓 여 결국 OOM 을 초래 할 수 있 습 니 다.최근 작업 중 링크 드 BlockingQueue 를 차단 대기 열 로 사용 하기 때문에 일부 작업 은 80s + 가 걸 리 고 새로운 작업 이 계속 들 어 와 cpu 와 메모리 가 급증 하여 서버 가 끊 겼 습 니 다.
2.2 유 계 대열
자주 사용 되 는 두 가지 유형 이 있 는데 하 나 는 FIFO 원칙 을 따 르 는 대기 열, 예 를 들 어 Array BlockingQueue 와 경계 가 있 는 링크 드 BlockingQueue 이 고 다른 하 나 는 Priority BlockingQueue 와 같은 우선 순위 대기 열 입 니 다.Priority BlockingQueue 의 우선 순 위 는 작업 의 Comparator 에 의 해 결 정 됩 니 다.경계 대기 열 을 사용 할 때 대기 열 크기 는 스 레 드 탱크 크기 와 서로 어 울 려 야 합 니 다. 스 레 드 탱크 가 비교적 작 을 때 메모리 소 모 를 줄 이 고 cpu 사용률 과 문맥 전환 을 낮 출 수 있 으 나 시스템 스루풋 을 제한 할 수 있 습 니 다.
2.3 동시 인도
대기 열 에서 작업 을 기다 리 지 않 고 작업 스 레 드 에 직접 넘 기 려 면 SynchronousQueue 를 대기 열 로 사용 할 수 있 습 니 다.SynchronousQueue 는 진정한 대기 열 이 아니 라 스 레 드 간 이 양 메커니즘 입 니 다.하나의 요 소 를 SynchronousQueue 에 넣 으 려 면 다른 스 레 드 가 이 요 소 를 받 기 를 기다 리 고 있어 야 합 니 다.무 계 스 레 드 탱크 나 포화 정책 을 사용 할 때 만 이 대기 열 을 사용 하 는 것 을 권장 합 니 다.
2.4 몇 가지 BlockingQueue 의 구체 적 인 실현 원리
상기 몇 가지 BlockingQueue 의 구체 적 인 실현 원리 와 분석 은 다음 블 로그 에서 상세 하 게 논술 할 것 이다.
3. 선택 가능 한 포화 정책 Rejected Execution Handler 상세 설명
JDK 는 주로 선택 할 수 있 도록 4 가지 포화 전략 을 제공 했다.네 가지 전략 은 모두 정적 내부 클래스 로 ThreadPoolExcutor 에서 이 루어 집 니 다.
3.1 AbortPolicy 중지 정책
이 정책 은 기본 포화 정책 이다.
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }

이 정책 을 사용 할 때 포화 상태 에 있 을 때 Rejected Execution Exception (Runtime Exception 계승) 을 던 집 니 다. 호출 자 는 이 이상 을 포착 하여 스스로 처리 할 수 있 습 니 다.
3.2 DiscardPolicy 포기 전략
  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }

코드 에서 보 듯 이 어떤 처리 도 하지 않 고 임 무 를 포기 합 니 다.
3.3 DiscardOldestPolicy 낡은 임무 전략 포기
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }

코드 와 같이 차단 대기 열 에 있 는 헤드 요 소 를 팀 에서 버 리 고 작업 을 제출 하려 고 합 니 다.이 때 차단 대기 열 이 Priority BlockingQueue 우선 순위 대기 열 을 사용 하면 우선 순위 가 가장 높 은 작업 이 버 려 질 수 있 으 므 로 이 정책 을 우선 순위 대기 열 에 맞 추 는 것 을 권장 하지 않 습 니 다.
3.4 CallerRunsPolicy 호출 자 실행
   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }

임 무 를 포기 하지 도 않 고 이상 을 던 지지 도 않 고 임 무 를 직접 수행 하 는 run 방법 은 호출 자 에 게 임 무 를 되 돌려 직접 실행 하 는 것 이다.이 정책 을 사용 할 때 스 레 드 탱크 가 포화 되면 스 레 드 탱크 의 메 인 스 레 드 가 스스로 임 무 를 수행 하기 때문에 작업 을 수행 하 는 동안 메 인 스 레 드 는 새로운 임 무 를 제출 할 수 없 기 때문에 스 레 드 탱크 에서 작업 스 레 드 가 처리 하고 있 는 임 무 를 처리 할 시간 이 있 습 니 다.
4. 자바 가 제공 하 는 네 가지 상용 스 레 드 풀 분석
JDK 도움말 문서 에 다음 과 같은 말 이 있 습 니 다.
프로그래머 가 비교적 편리 한 Executors 공장 방법 Executors. new CachedThreadPool () (무 계 스 레 드 풀, 자동 스 레 드 회수 가능), Executors. new Fixed ThreadPool (int) (고정 크기 스 레 드 풀) Executors. new Single ThreadExecutor () (단일 백 스테이지 스 레 드) 를 사용 하 는 것 을 강력 히 권장 합 니 다.
상술 한 네 가지 스 레 드 탱크 를 상세 하 게 소개 하 다.
4.1 newCachedThreadPool
     public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

new Cached ThreadPool 에서 스 레 드 풀 의 길이 가 처리 수 요 를 초과 하면 남 은 스 레 드 를 유연 하 게 회수 할 수 있 고 회수 할 수 없 으 면 새 스 레 드 를 만 들 수 있 습 니 다.이 구조 함 수 를 처음 봤 을 때 저 는 이런 의혹 이 있 었 습 니 다. 핵심 스 레 드 탱크 는 0 입 니 다. 앞에서 말 한 스 레 드 탱크 전략 에 따라 새로운 작업 이 왔 을 때 핵심 스 레 드 탱크 에 들 어 갈 수 없고 SynchronousQueue 에 들 어가 서 만 기다 릴 수 있 습 니 다. SynchronousQueue 의 크기 는 1 입 니 다. 첫 번 째 작업 이 도 착 했 을 때 대기 열 에서 만 기다 릴 수 있 는 것 이 아 닙 니까?두 번 째 작업 이 도착 할 때 까지 대기 열 에 들 어 갈 수 없 음 을 발견 해야만 첫 번 째 스 레 드 를 만 들 수 있 습 니까?이 문제 의 답 은 위 에서 SynchronousQueue 라 고 말 했 을 때 이미 제시 되 었 습 니 다. 하나의 요 소 를 SynchronousQueue 에 넣 으 려 면 다른 스 레 드 가 이 요 소 를 받 기 를 기다 리 고 있어 야 합 니 다.따라서 SynchronousQueue 가 처음에는 비어 있 고 크기 가 1 이 더 라 도 첫 번 째 작업 은 넣 을 수 없습니다. SynchronousQueue 에서 요 소 를 가 져 오 기 를 기다 리 는 라인 이 없 기 때 문 입 니 다.따라서 첫 번 째 작업 이 도착 하면 새 스 레 드 를 만들어 이 작업 을 수행 합 니 다.여기 서 작은 기 교 를 설명 합 니 다. 때때로 우 리 는 스 레 드 탱크 가 작업 이 없 는 상태 에서 모든 스 레 드 를 없 애 기 를 바 랄 수도 있 습 니 다. 스 레 드 탱크 의 핵심 크기 는 0 이지 만 SynchronousQueue 를 사용 하지 않 고 경계 있 는 대기 열 을 사용 하려 고 할 수도 있 습 니 다.특별한 설정 을 하지 않 으 면 이상 한 행동 이 발생 할 수 있 습 니 다. 대기 열 이 채 워 져 야 새 스 레 드 가 생 성 되 고 작업 이 시 작 됩 니 다.이것 은 우리 가 원 하 는 것 이 아 닙 니 다. 이 때 allow CoreThreadTimeOut 을 통 해 대기 열 에 있 는 요 소 를 팀 에서 호출 하여 실행 할 수 있 습 니 다. 상세 한 원리 와 사용 은 후속 블 로그 에서 설명 할 것 입 니 다.
4.2 new Fixed ThreadPool 은 고정 라인 풀 을 만 들 고 스 레 드 의 최대 병발 수 를 제어 할 수 있 으 며 초과 한 스 레 드 는 대기 열 에서 기다 릴 수 있 습 니 다.
  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }

코드 를 보면 한눈 에 알 수 있 습 니 다. 고정 크기 의 스 레 드 탱크 를 사용 하고 무한 한 대기 열 을 사용 합 니 다.
4.3 new Scheduled ThreadPool 은 정기 적 이 고 주기 적 인 작업 수행 을 지원 하 는 정기 적 인 라인 풀 을 만 듭 니 다.
   public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

Scheduled Thread PoolExecutor () 의 구조 함 수 를 살 펴 보 겠 습 니 다.
  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

Scheduled Thread PoolExecutor 의 부모 클래스 는 Thread PoolExecutor 이기 때문에 각 매개 변수의 의 미 는 위 와 같 습 니 다.관심 사 는 Delayed WorkQueue 라 는 차단 열 입 니 다. 위 에 소개 되 지 않 았 습 니 다. 정적 내부 클래스 로 Scheduled Thread PoolExecutor 에서 이 루어 졌 습 니 다.구체 적 인 분석 은 후속 블 로그 에서 설명 할 것 이다. 여기 서 간단 한 설명 만 할 수 있다. Delayed WorkQueue 는 무한 대기 열 로 작업 대기 열 에 있 는 요 소 를 일정한 순서에 따라 배열 할 수 있다.그래서 여기에 설 치 된 최대 스 레 드 수 Integer. MAXVALUE 는 아무런 의미 가 없다.Scheduled Thread PoolExecutor 의 구체 적 인 사용 은 후속 quartz 의 주기 적 임무 실현 원리 에서 진일보 한 분석 을 할 것 이다.
4.4 new Single ThreadExecutor 는 단일 라인 화 된 스 레 드 풀 을 만 듭 니 다. 유일한 작업 스 레 드 로 만 작업 을 수행 하고 모든 작업 이 지 정 된 순서 (FIFO, LIFO, 우선 순위) 에 따라 실 행 될 수 있 도록 합 니 다.
   public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

먼저 new 는 스 레 드 수가 1 인 Scheduled Thread PoolExecutor 를 만 든 다음 에 이 대상 을 Delegated Scheduled Executor Service 에 전송 하여 Delegated Scheduled Executor Service 의 실현 코드 를 보십시오.
 DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
            super(executor);
            e = executor;
        }

부 류 를 보고 있 습 니 다.
 DelegatedExecutorService(ExecutorService executor) { e = executor; }

사실은 장식 모드 를 사용 하여 ScheduledExecutorService (1) 의 기능 을 강화 하여 하나의 스 레 드 순서 로 임 무 를 수행 할 수 있 을 뿐만 아니 라 스 레 드 가 예상 치 못 하 게 종 료 된 후에 하나의 스 레 드 를 다시 만들어 임 무 를 계속 수행 할 수 있 도록 하 는 것 입 니 다.구체 적 인 실현 원 리 는 후속 블 로그 에서 설명 한다.
4.5 new Work Stealing Pool 은 연결 수 를 줄 이기 위해 여러 작업 대기 열 을 가 진 스 레 드 풀 을 만 듭 니 다.
이것 은 jdk 1.8 에서 새로 추 가 된 스 레 드 탱크 의 실현 입 니 다. 먼저 무 참 실현 을 살 펴 보 겠 습 니 다.
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

돌아 오 는 포크 조 인 트 풀 은 jdk 1.7 부터 도입 해 개인 적 으로 마 프 리 듀 싱 과 비슷 한 느낌 을 준다.이 스 레 드 탱크 는 비교적 특수 하 므 로 후속 블 로그 에서 상세 한 사용 설명 과 원 리 를 제시 할 것 이다.

좋은 웹페이지 즐겨찾기