왜 알리바바는Executors를 사용하지 않고 스레드 탱크를 만듭니까?

알리바바 개발 매뉴얼을 보고 프로그래밍을 하는 것은 다음과 같다. 스레드 탱크는 Executors를 사용하여 만들 수 없고 ThreadPool Executor를 통해 원본 코드를 통해 사용하지 않는 원인을 분석한다.

앞에 쓰다


이 글을 읽으면 다음과 같은 내용을 알 수 있습니다.
  • 스레드 풀의 정의
  • Executors에서 스레드 풀을 만드는 몇 가지 방식
  • ThreadPoolExecutor 객체
  • 스레드 탱크 실행 작업 논리와 스레드 탱크 파라미터의 관계
  • Executors 만들기 ThreadPoolExecutor 객체 반환
  • OOM 예외 테스트
  • 스레드 탱크 파라미터를 어떻게 정의하는가
  • 이유만 알고 싶으면 총결산까지 끌어올릴 수 있어요.

    스레드 탱크의 정의


    일련의 작업 라인을 관리하다.스레드 풀을 통해 스레드를 재사용할 경우 다음과 같은 이점이 있습니다.
  • 리소스 생성 감소 => 메모리 오버헤드 감소, 스레드 생성 차지 메모리
  • 시스템 비용 절감 =>스레드 생성 소요 시간, 처리 지연 요청
  • 안정성 향상 = > 무한 생성 스레드로 인한 OutOfMemoryError[약칭 OOM]
  • 방지

    Executors에서 스레드 풀을 만드는 방법


    반환된 객체 유형에 따라 스레드 풀을 만드는 세 가지 유형은 다음과 같습니다.
  • 만들기 ThreadPoolExecutor 객체 반환
  • 생성 반환ScheduleThreadPoolExecutor 대상
  • 만들기ForkJoinPool 객체 반환
  • 이 문서는 ThreadPoolExecutor로 돌아가는 대상만 만듭니다

    ThreadPoolExecutor 객체


    Executors에서 스레드 탱크를 만드는 방법을 소개하기 전에 ThreadPoolExecutor를 소개합니다. 이런 스레드 탱크를 만드는 정적 방법은 ThreadPoolExecutor 대상을 되돌려주는 것이기 때문입니다. 우리가 수동으로 ThreadPoolExecutor 대상을 만드는 것과 다른 것은 우리가 구조 함수를 전달할 필요가 없는 매개 변수입니다.
    ThreadPoolExecutor의 구조 함수는 모두 네 개이지만 최종적으로 호출된 함수는 모두 같다.
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    

    구조 함수 매개변수 설명:
  • corePoolSize = > 스레드 풀 코어 스레드 수
  • maximumPoolSize=>스레드 풀 최대 수
  • keepAliveTime=>유휴 스레드 생존 시간
  • unit=>시간 단위
  • workQueue=>스레드 풀에 사용되는 버퍼 대기열
  • threadFactory=>스레드 탱크에서 스레드를 만드는 공장
  • handler=>스레드 탱크의 작업 거부 처리 정책
  • 스레드 탱크 실행 작업 논리와 스레드 탱크 파라미터의 관계
    논리 설명을 실행하려면 다음과 같이 하십시오.
  • 핵심 스레드 수가 가득 찼는지 판단하고, 핵심 스레드 수 크기는 코어Poolsize 파라미터와 관계가 있으며, 가득 차지 않으면 스레드 생성 작업 수행
  • 핵심 스레드 탱크가 가득 차면 대기열이 꽉 차는지 판단하고workQueue 파라미터와 관련이 있으며 꽉 차지 않으면 대기열에 가입
  • 대기열이 가득 차면 스레드 풀이 가득 찼는지 판단하고, 스레드 풀이 가득 찼는지 여부는maximumPoolSize 파라미터와 관계가 있으며, 스레드를 가득 채우지 않으면 작업을 수행합니다
  • 스레드 탱크가 가득 차면 실행 불가능한 작업을 거부 정책으로 처리합니다. 거부 정책은handler 파라미터와 관련이 있습니다
  • Executors 만들기 ThreadPoolExecutor 객체 반환


    Executors는 ThreadPoolExecutor 객체를 반환하는 세 가지 방법으로 작성됩니다.
  • Executors#newCachedThreadPool=>캐시 가능한 스레드 풀 만들기
  • Executors#newSingleThreadExecutor=>단일 스레드의 스레드 풀 만들기
  • Executors#newFixedThreadPool=>고정된 길이의 스레드 풀 만들기
  • Executors#newCachedThreadPool 방법
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }
    

    CachedThreadPool은 필요에 따라 새 스레드를 만드는 스레드 풀입니다.
  • corePoolSize => 0, 코어 스레드 풀 수 0
  • maximumPoolSize => Integer.MAX_VALUE, 최대 스레드 수는 무제한으로 간주
  • keepAliveTime => 60L
  • unit=>초
  • workQueue => SynchronousQueue

  • 임무를 제출할 때 코어 Pool Size는 0으로 핵심 라인을 만들지 않습니다. SynchronousQueue는 요소를 저장하지 않는 대기열로 팀이 영원히 꽉 차 있기 때문에 최종적으로 비핵심 라인을 만들어서 임무를 수행합니다.
    비핵심 라인이 60s 비어 있을 때 회수됩니다.Integer 때문에.MAX_VALUE는 매우 커서 무한히 라인을 만들 수 있다고 여길 수 있으며 자원이 제한된 상황에서 OOM 이상을 일으키기 쉽다
    Executors#newSingleThreadExecutor 방법
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }
    

    SingleThreadExecutor는 단일 스레드 스레드 풀로 코어 스레드 하나만 있음
  • corePoolSize => 1, 코어 스레드 풀 수 1
  • maximumPoolSize=>1, 비핵심 스레드 하나만 만들 수 있음
  • keepAliveTime => 0L
  • unit=>초
  • workQueue => LinkedBlockingQueue

  • 작업이 제출되면 먼저 핵심 라인을 만들어서 작업을 수행합니다. 핵심 라인의 수량을 초과하면 대기열에 넣습니다. 링크드 블록 Queue는 Integer이기 때문입니다.MAX_VALUE의 대기열은 무계 대기열이라고 할 수 있기 때문에 대기열에 무한한 많은 작업을 삽입할 수 있습니다. 자원이 제한될 때 OOM 이상을 일으키기 쉽습니다. 또한 무계 대기열로 인해maximumPoolsize와keepAliveTime 파라미터는 무효가 되고 비핵심 라인을 만들지 않습니다.
    Executors#newFixedThreadPool 방법
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
    

    FixedThreadPool은 고정 코어 스레드 풀로, 고정 코어 스레드 수는 사용자가 전송
  • corePoolSize => 1, 코어 스레드 풀 수 1
  • maximumPoolSize=>1, 비핵심 스레드 하나만 만들 수 있음
  • keepAliveTime => 0L
  • unit=>초
  • workQueue => LinkedBlockingQueue
  • 이것은singleThreadExecutor와 유사한데 유일한 차이점은 핵심 스레드 수가 다르다는 것이다. 또한LinkedBlockingQueue를 사용하기 때문에 자원에 한계가 있을 때 OOM이상을 일으키기 쉽다
  • 요약:

  • FixedThreadPool과SingleThreadExecutor=>허용된 요청 대기열 길이는 Integer입니다.MAX_VALUE, 많은 요청이 쌓여 OOM 예외가 발생할 수 있음
  • CachedThreadPool=>생성할 수 있는 스레드 수는 Integer입니다.MAX_VALUE, 대량의 스레드를 생성하여 OOM 이상을 일으킬 수 있음
  • 이것이 바로 Executors를 사용하여 스레드 풀을 만드는 것을 금지하고 ThreadPoolExecutor를 만드는 것을 추천하는 이유입니다

    OOM 예외 테스트


    이론적으로 OOM 예외가 발생할 수 있으므로 한 차례의 검증 전 표현을 테스트해야 합니다.
    테스트 클래스: TaskTest.java
    public class TaskTest {
        public static void main(String[] args) {
            ExecutorService es = Executors.newCachedThreadPool();
            int i = 0;
            while (true) {
                es.submit(new Task(i++));
            }
        }
    }
    

    Executors에서 만든 CachedThreadPool을 사용하여 스레드 풀에 스레드를 무제한으로 추가
    테스트 클래스를 시작하기 전에 JVM 메모리를 좀 작게 조정해야 한다. 그렇지 않으면 컴퓨터를 쉽게 문제를 일으킬 수 있다. [왜 알았는지 묻지 마라. 철바보가 틀림없다!!!]아이디어에서: Run -> Edit ConfigurationsJVM 매개 변수 설명: --Xms10M =>Java Heap 메모리 초기화 값
  • - Xmx10M => Java Heap 최대 메모리
  • 실행 결과:
    Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
    Disconnected from the target VM, address: '127.0.0.1:60416', transport: 'socket'
    

    3w 여러 개의 라인을 만들 때 OOM 오류를 보고하기 시작합니다. 다른 두 개의 라인 탱크는 테스트를 하지 않습니다. 테스트 방법은 일치하지만 생성된 라인 탱크는 같지 않습니다.

    스레드 탱크 파라미터를 어떻게 정의합니까

  • CPU 집약형 = > 스레드 풀 크기는 CPU 수량 + 1로 권장 CPU 수량Runtime.availableProcessors 방법에 따라
  • IO 집약형 =>CPU 수량*CPU 활용도*(1 + 스레드 대기 시간/스레드 CPU 시간)
  • 블렌드 => 작업을 CPU 밀집형과 IO 밀집형으로 나누고 각각 다른 스레드 풀로 처리하여 각 스레드 풀을 각자의 작업 부하에 따라 조정할 수 있도록 한다
  • 차단 대기열=>유계 대기열을 추천합니다. 유계 대기열은 자원 소모를 피하는 데 도움이 됩니다
  • 거부 정책 =>기본값은 AbortPolicy 거부 정책을 사용하고 프로그램에서 직접 RejectedExecutionException 이상을 던집니다. [실행 시 이상이기 때문에 강제하지 않습니다catch]. 이런 처리 방식은 우아하지 않습니다.처리 거부 정책에는 다음과 같은 비교 권장 사항이 있습니다.
  • 프로그램에서 포획RejectedExecutionException이상을 포획하고 포획이상에서 임무를 처리한다.기본 거부 정책용
  • 거부 정책을 사용합니다. 이 정책은execute를 호출한 라인에 임무를 맡깁니다 [일반 메인 라인]. 이 때 메인 라인은 일정 시간 동안 어떤 작업도 제출할 수 없기 때문에 작업 라인이 실행 중인 작업을 처리합니다.이때 커밋된 스레드가 CallerRunsPolicy 대기열에 저장되고 TCP 대기열이 가득 차면 클라이언트에게 영향을 주며 느린 성능 저하
  • 사용자 정의 거부 정책, TCP 인터페이스만 있으면 된다
  • 임무가 특별히 중요하지 않으면 RejectedExecutionHandlerDiscardPolicy 거부 정책을 사용하여 임무를 버려도 된다

  • 만약에 Executors의 정적 방법으로 DiscardOldestPolicy 대상을 만들면 ThreadPoolExecutor를 사용하여 임무의 집행을 제한할 수 있고 Semaphore 이상이 발생하지 않도록 할 수 있다. 왜냐하면 스레드 탱크의 매개 변수에 대한 정의 경험이 적기 때문에 모두 이론 지식이다. 경험이 있는 남자가 보충하는 것을 환영한다.
    저자: 하달달이가 있나 주진아.im/post/5dc41c165188257bad4d9e69

    좋은 웹페이지 즐겨찾기