Java 프로그래밍의 스레드 풀을 간단히 소개합니다.

4840 단어 Java라인
Java 5부터 Java는 자체 스레드 풀을 제공합니다.스레드 탱크는 하나의 스레드 용기로 매번 정해진 수량의 스레드만 실행한다.java.util.concurrent.ThreadPoolExecutor가 바로 이러한 스레드 풀입니다.그것은 매우 유연하지만 사용하기도 비교적 복잡하기 때문에 본고는 그것에 대해 소개한다.
우선 구조 함수다.가장 간단한 구조 함수를 예로 들면:

public ThreadPoolExecutor( 
      int corePoolSize, 
      int maximumPoolSize, 
      long keepAliveTime, 
      TimeUnit unit, 
      BlockingQueue<Runnable> workQueue) 
복잡해 보여요.여기 소개해 드릴게요.
corePoolSize는 스레드 풀 크기를 유지합니다.
maximumPoolSize는 스레드 풀의 최대 크기를 나타냅니다.
keepaliveTime은 유휴 스레드가 끝나는 시간 초과 시간을 의미합니다.
unit는 keepaliveTime을 나타내는 매개 단위입니다.
workQueue는 작업을 저장하는 대기열을 나타냅니다.
우리는 스레드 탱크의 작업 과정에서 이러한 매개 변수의 의미를 이해할 수 있다.스레드 풀의 작업 과정은 다음과 같습니다.
1. 스레드 탱크가 처음 만들어졌을 때 안에 스레드가 하나도 없었다.작업 대기열은 매개 변수로 전송됩니다.그러나 대열에 임무가 있어도 스레드 탱크는 곧바로 수행하지 않는다.
 
2. execute () 방법으로 작업을 추가할 때 스레드 탱크는 다음과 같은 판단을 합니다.
a. 실행 중인 스레드 수량이corePoolSize보다 작으면 바로 이 작업을 실행할 스레드를 만듭니다.
b. 실행 중인 스레드의 수량이 코어풀 크기보다 크거나 같으면 이 작업을 대기열에 넣습니다.
c. 이때 대기열이 가득 차고 실행 중인 스레드 수량이 maximumPoolSize보다 적으면 이 작업을 실행할 스레드를 만듭니다.
d. 대기열이 꽉 차고 실행 중인 스레드의 수량이 maximumPoolSize보다 크거나 같으면 스레드 탱크에서 이상을 던져 호출자에게 "나는 더 이상 작업을 받아들일 수 없다"고 알려줍니다.
 
3. 한 라인이 임무를 완성하면 대기열에서 다음 작업을 가져옵니다.
 
4. 한 라인이 할 일이 없고 일정한 시간(keep Alive Time)을 초과할 때 라인 탱크는 현재 실행 중인 라인 수가corePoolSize보다 많으면 이 라인이 정지된다고 판단합니다.따라서 스레드 풀의 모든 작업이 완료되면 코어 Pool Size 크기로 축소됩니다.
 
이런 과정은 임무에 먼저 가입하면 반드시 먼저 수행하는 것이 아니라는 것을 설명한다.대기열 크기가 10이고 코어 풀 사이즈가 3, maximum Pool Size가 6이라고 가정하면 20개의 작업을 추가할 때 수행하는 순서는 다음과 같다. 먼저 작업 1, 2, 3을 수행하고 작업 4~13을 대기열에 넣는다.이때 대열이 꽉 차면 퀘스트 14, 15, 16이 즉시 실행되고, 퀘스트 17~20은 이상이 발생합니다.최종 순서는 1, 2, 3, 14, 15, 16, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13이다.다음은 스레드 풀에서 사용하는 예입니다.

public static void main(String[] args) { 
  BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); 
  ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue); 
  
  for (int i = 0; i < 20; i++) { 
    executor.execute(new Runnable() { 
  
      public void run() { 
        try { 
          Thread.sleep(1000); 
        } catch (InterruptedException e) { 
          e.printStackTrace(); 
        } 
        System.out.println(String.format("thread %d finished", this.hashCode())); 
      } 
    }); 
  } 
  executor.shutdown(); 
} 

이 예에 대한 설명은 다음과 같습니다.
 
1. BlockingQueue는 하나의 인터페이스일 뿐, 자주 사용하는 실현 클래스는 LinkedBlockingQueue와 ArrayBlockingQueue가 있다.LinkedBlockingQueue를 사용하면 크기 제한이 없다는 장점이 있습니다.이렇게 하면 대기열이 가득 차지 않기 때문에execute () 는 이상을 던지지 않으며, 스레드 탱크에서 실행되는 스레드 수는corePoolSize 개를 영원히 초과하지 않으며,keepaliveTime 매개 변수도 의미가 없습니다.
 
2. shutdown () 방법은 막히지 않습니다.shutdown () 방법을 호출하면 메인 스레드가 바로 끝나고, 모든 작업이 끝날 때까지 스레드 탱크가 계속 실행됩니다.shutdown () 방법을 사용하지 않으면, 언제든지 새로운 작업을 추가할 수 있도록 스레드 탱크를 계속 유지합니다.
 
여기까지 이 스레드 탱크에 대해 아직 일부분만 소개했을 뿐이다.ThreadPoolExecutor는 확장성이 강하지만, 확장의 전제 조건은 작업 방식을 익히는 것이다.다음 글은 ThreadPoolExecutor 클래스를 확장하는 방법을 소개합니다.
ava.util.concurrent.ThreadPoolExecutor 클래스는 다양한 확장성을 제공합니다.하위 클래스를 만들어서 행동을 사용자 정의할 수 있습니다.예를 들어, 나는 모든 작업이 끝난 후에 메시지를 인쇄하고 싶지만, 작업 대상을 수정할 수 없다. 그러면 나는 이렇게 쓸 수 있다.

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    System.out.println("Task finished.");
  }
};
afterExecute 방법 외에도ThreadPoolExecutor 클래스는 beforeExecute () 와terminated () 방법으로 다시 쓸 수 있습니다. 각각 작업이 실행되기 전과 전체 스레드 탱크가 정지된 후에 실행됩니다.
작업 실행 전후의 동작을 추가할 수 있는 것 외에도ThreadPoolExecutor는 작업 추가가 실패한 후의 실행 정책을 사용자 정의할 수 있습니다.스레드 풀의 set Rejected Execution Handler () 방법을 호출하여 사용자 정의 Rejected Execution Handler 대상으로 기존 정책을 대체할 수 있습니다.ThreadPoolExecutor는 다음과 같은 4가지 기존 정책을 제공합니다.
ThreadPoolExecutor.AbortPolicy: 퀘스트를 거부하고 이상을 던진다
ThreadPoolExecutor.DiscardPolicy: 작업을 거부하지만 아무 작업도 하지 않음
ThreadPoolExecutor.CallerRunsPolicy: 작업을 거부하고 호출자의 라인에서 이 작업을 직접 수행합니다.
ThreadPoolExecutor.Discard Oldest Policy: 작업 대기열의 첫 번째 작업을 버리고 이 작업을 대기열에 추가합니다.
이것은 하나의 예입니다.
ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
이외에도 Rejected ExecutionHandler 인터페이스를 실현하여 자신의 정책을 작성할 수 있습니다.다음은 예입니다.

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
    new RejectedExecutionHandler() {
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println(String.format("Task %d rejected.", r.hashCode()));
      }
    }
);

좋은 웹페이지 즐겨찾기