Java 스레드 풀 상세 정보

10934 단어 Java스레드 탱크
시스템이 하나의 스레드를 시작하는 비용은 비교적 높다. 왜냐하면 이것은 운영체제와의 상호작용과 관련되기 때문이다. 스레드 풀을 사용하는 장점은 성능을 향상시키는 것이다. 시스템에 대량의 병발 스레드가 포함될 때 시스템 성능이 급격히 떨어지고 심지어는 JVM이 붕괴될 수도 있다. 또한 스레드 풀의 최대 스레드 수 파라미터는 시스템의 병발 스레드 수가 횟수를 초과하지 않도록 제어할 수 있다.
1. Executors 공장 클래스는 스레드 탱크를 만드는 데 사용되며, 이 공장 클래스는 다음과 같은 몇 가지 정적 공장 방법으로 대응하는 스레드 탱크를 만드는 것을 포함한다.생성된 스레드 탱크는 Executor Service 대상입니다. 이 대상의submit 방법이나 execute 방법으로 해당하는 Runnable 또는 Callable 작업을 수행합니다.스레드 탱크 자체가 더 이상 필요하지 않을 때shutdown () 방법을 호출하여 스레드 탱크를 정지합니다. 이 방법을 호출하면 이 스레드 탱크는 작업을 추가하는 것을 허용하지 않지만, 추가된 모든 작업이 완료될 때까지 사망합니다.
1. newCachedThreadPool(), 캐시 기능이 있는 스레드 풀을 만들고 이 스레드 풀에 제출된 작업(Runnable 또는 Callable 대상)이 만든 스레드를 실행합니다. 실행이 완료되면 뒤에 수행해야 할 작업에 캐시됩니다.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CacheThreadPool {
  static class Task implements Runnable {
    @Override
    public void run() {
      System.out.println(this + " " + Thread.currentThread().getName() + " AllStackTraces map size: "
          + Thread.currentThread().getAllStackTraces().size());
    }
  }

  public static void main(String[] args) {
    ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
    
    // 
    for(int i = 0 ; i < 3; i++) {
      cacheThreadPool.execute(new Task());
    }
    
    // , 
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    
    for(int i = 0 ; i < 3; i++) {
      cacheThreadPool.execute(new Task());
    }
  }

}

실행 결과는 다음과 같습니다.

CacheThreadPool$Task@2d312eb9 pool-1-thread-1 AllStackTraces map size: 7
CacheThreadPool$Task@59522b86 pool-1-thread-3 AllStackTraces map size: 7
CacheThreadPool$Task@73dbb89f pool-1-thread-2 AllStackTraces map size: 7
CacheThreadPool$Task@5795cedc pool-1-thread-3 AllStackTraces map size: 7
CacheThreadPool$Task@256d5600 pool-1-thread-1 AllStackTraces map size: 7
CacheThreadPool$Task@7d1c5894 pool-1-thread-2 AllStackTraces map size: 7

스레드 탱크의 스레드 대상이 캐시되어 새 작업이 실행될 때 다시 사용됩니다.그러나 특히 많은 병발이 있을 때, 캐시 스레드 탱크는 많은 스레드 대상을 만들 것이다.
2. newFixedThreadPool(int nThreads)은 지정된 스레드 개수를 만들고 스레드를 다시 사용할 수 있는 스레드 탱크를 만듭니다.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPool {
  static class Task implements Runnable {
    @Override
    public void run() {
      System.out.println(this + " " + Thread.currentThread().getName() + " AllStackTraces map size: "
          + Thread.currentThread().getAllStackTraces().size());
    }
  }

  public static void main(String[] args) {
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

    //  
    for (int i = 0; i < 5; i++) {
      fixedThreadPool.execute(new Task());
    }

    //  , 
    try {
      Thread.sleep(3);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    for (int i = 0; i < 3; i++) {
      fixedThreadPool.execute(new Task());
    }
  }

}

실행 결과:

FixedThreadPool$Task@7045c12d pool-1-thread-2 AllStackTraces map size: 7
FixedThreadPool$Task@50fa0bef pool-1-thread-2 AllStackTraces map size: 7
FixedThreadPool$Task@ccb1870 pool-1-thread-2 AllStackTraces map size: 7
FixedThreadPool$Task@7392b4e3 pool-1-thread-1 AllStackTraces map size: 7
FixedThreadPool$Task@5bdeff18 pool-1-thread-2 AllStackTraces map size: 7
FixedThreadPool$Task@7d5554e1 pool-1-thread-1 AllStackTraces map size: 7
FixedThreadPool$Task@24468092 pool-1-thread-3 AllStackTraces map size: 7
FixedThreadPool$Task@fa7b978 pool-1-thread-2 AllStackTraces map size: 7

3. newSingleThreadExecutor(), newFixedThreadPool을 호출하는 것과 같은 단일 스레드 풀을 만듭니다.
4. newSheduledThreadPool(intcorePoolSize), 지정한 스레드 수의 스레드 탱크를 만듭니다. 지정한 딜레이 후에 스레드를 실행할 수 있습니다.어떤 주기로 어떤 라인을 반복해서 실행할 수도 있습니다. shutdown () 을 호출하여 스레드 탱크를 닫는 것을 알고 있습니다.
예는 다음과 같습니다.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPool {
  static class Task implements Runnable {
    @Override
    public void run() {
      System.out.println("time " + System.currentTimeMillis() + " " + Thread.currentThread().getName() + " AllStackTraces map size: "
          + Thread.currentThread().getAllStackTraces().size());
    }
  }

  public static void main(String[] args) {
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
    
    scheduledExecutorService.schedule(new Task(), 3, TimeUnit.SECONDS);
    
    scheduledExecutorService.scheduleAtFixedRate(new Task(), 3, 5, TimeUnit.SECONDS);
  
    try {
      Thread.sleep(30 * 1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    scheduledExecutorService.shutdown();
  }

}

실행 결과는 다음과 같습니다.

time 1458921795240 pool-1-thread-1 AllStackTraces map size: 6
time 1458921795241 pool-1-thread-2 AllStackTraces map size: 6
time 1458921800240 pool-1-thread-1 AllStackTraces map size: 7
time 1458921805240 pool-1-thread-1 AllStackTraces map size: 7
time 1458921810240 pool-1-thread-1 AllStackTraces map size: 7
time 1458921815240 pool-1-thread-1 AllStackTraces map size: 7
time 1458921820240 pool-1-thread-1 AllStackTraces map size: 7

운행 시간에서 알 수 있듯이 임무는 5초의 주기에 따라 집행된다.
5. newSingleThreadScheduledExecutor()는 하나의 라인만 있는 라인 풀을 만들고 newScheduledThreadPool(1)을 함께 사용합니다.
2. ForkJoinPool 및 ForkJoinTask
ForkJoinPool은 Executor Service의 실현 클래스로 하나의 임무를 여러 개의 작은 임무로 나누어 병렬적으로 계산하고 여러 개의 작은 임무의 계산 결과를 전체적인 계산 결과로 통합하는 것을 지원합니다.그것은 두 개의 구조 함수가 있다
ForkJoinPool(int parallelism) 병렬 라인을 포함하는 ForkJoinPool을 만듭니다.
ForkJoinPool(), Runtime.availableProcessors() 메서드는 parallelism 매개 변수로 값을 되돌려 ForkJoinPool을 생성합니다.
ForkJoinTask는 병렬적이고 통합된 작업을 나타냅니다.이것은 Future 인터페이스를 실현한 추상적인 클래스입니다. 두 개의 추상적인 하위 클래스가 있습니다. 되돌아오는 값 작업이 없는 Recurive Action과 되돌아오는 값이 있는 Recursive Task를 대표합니다.구체적인 수요에 따라 이 두 추상적인 클래스를 계승하여 자신의 대상을 실현하고 ForkJoinPool의submit 방법으로 실행할 수 있습니다.
Recurive Action의 예는 0-300의 숫자를 병렬 출력하는 것입니다.

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;

public class ActionForkJoinTask {
  static class PrintTask extends RecursiveAction {
    private static final int THRESHOLD = 50;
    private int start;
    private int end;

    public PrintTask(int start, int end) {
      this.start = start;
      this.end = end;
    }

    @Override
    protected void compute() {
      if (end - start < THRESHOLD) {
        for(int i = start; i < end; i++) {
          System.out.println(Thread.currentThread().getName() + " " + i);
        }
      } else {
        int middle = (start + end) / 2;
        PrintTask left = new PrintTask(start, middle);
        PrintTask right = new PrintTask(middle, end);
        left.fork();
        right.fork();
      }
    }

  }

  public static void main(String[] args) {
    ForkJoinPool pool = new ForkJoinPool();
    
    pool.submit(new PrintTask(0, 300));
    try {
      pool.awaitTermination(2, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    
    pool.shutdown();
  }

}

작은 작업을 분할한 후 작업의 fork() 방법을 호출하여 ForkJoinPool에 추가하여 병행합니다.
RecursiveTask 예제: 100개의 정수 구합을 병렬 계산합니다.20개의 수구화로 나누어 결과를 얻고 마지막에 마지막 결과로 합친다.

import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public class TaskForkJoinTask {
  static class CalTask extends RecursiveTask<Integer> {
    private static final int THRESHOLD = 20;

    private int arr[];
    private int start;
    private int end;

    public CalTask(int[] arr, int start, int end) {
      this.arr = arr;
      this.start = start;
      this.end = end;
    }

    @Override
    protected Integer compute() {
      int sum = 0;

      if (end - start < THRESHOLD) {
        for (int i = start; i < end; i++) {
          sum += arr[i];
        }
        System.out.println(Thread.currentThread().getName() + " sum:" + sum);
        return sum;
      } else {
        int middle = (start + end) / 2;
        CalTask left = new CalTask(arr, start, middle);
        CalTask right = new CalTask(arr, middle, end);

        left.fork();
        right.fork();

        return left.join() + right.join();
      }
    }

  }

  public static void main(String[] args) {
    int arr[] = new int[100];
    Random random = new Random();
    int total = 0;

    for (int i = 0; i < arr.length; i++) {
      int tmp = random.nextInt(20);
      total += (arr[i] = tmp);
    }
    System.out.println("total " + total);

    ForkJoinPool pool = new ForkJoinPool(4);

    Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length));
    try {
      System.out.println("cal result: " + future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
    pool.shutdown();
  }

}

실행 결과는 다음과 같습니다.

total 912
ForkJoinPool-1-worker-2 sum:82
ForkJoinPool-1-worker-2 sum:123
ForkJoinPool-1-worker-2 sum:144
ForkJoinPool-1-worker-3 sum:119
ForkJoinPool-1-worker-2 sum:106
ForkJoinPool-1-worker-2 sum:128
ForkJoinPool-1-worker-2 sum:121
ForkJoinPool-1-worker-3 sum:89
cal result: 912

하위 작업이 끝난 후, 작업의join () 방법을 호출하여 하위 작업 실행 결과를 얻고 마지막 결과를 얻습니다.

좋은 웹페이지 즐겨찾기