>  기사  >  Java  >  Java 스레드 풀을 만드는 방법

Java 스레드 풀을 만드는 방법

PHPz
PHPz앞으로
2023-05-16 08:43:123248검색

스레드 풀의 장점

  • 스레드 재사용을 실현하고 스레드 재생성 및 파괴를 방지할 수 있습니다. 스레드를 생성하고 스레드를 파괴하는 것은 CPU에 있어서 매우 비용이 많이 듭니다.

  • 생성할 수 있는 최대 스레드 수를 제한하고, 자체 시스템 성능에 따라 스레드 풀 매개변수를 동적으로 조정하여 애플리케이션 성능을 향상시킬 수 있습니다.

  • 예약 실행, 동시성 제어 등의 기능을 제공합니다.

  • 스레드 관리가 통합되었습니다.

스레드 풀을 생성하는 다섯 가지 방법

1: 캐시 스레드 풀(권장하지 않음)

2: 고정 용량 스레드 풀(권장하지 않음)

3: 단일 스레드 풀(권장하지 않음)

4: 타이밍 작업 스레드 풀(권장하지 않음)

5: ThreadPoolExecutor 구성 방법을 통해 스레드 풀 생성(Alibaba 개발 매뉴얼에서 적극 권장)

스레드 풀을 생성하는 처음 네 가지 방법은 모두 Executors의 정적 메서드를 통해 생성됩니다.

캐시된 ThreadPool

	ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            executorService.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
                }
            });
        }

캐시된 스레드 풀이 권장되지 않는 이유는 무엇인가요?

소스 코드 분석

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
    }
 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
    }

위의 두 코드 조각을 통해 CachedThreadPool의 maximumPoolSize는 Integer 2147483647의 최대값으로 무제한 스레드 생성과 동일하며, 스레드 생성에는 메모리가 필요하므로 메모리 오버플로가 발생함을 알 수 있습니다. , 그리고 일반 기계는 그렇게 많은 수의 스레드를 생성하기 위해 그렇게 큰 메모리를 사용하지 않습니다.

고정 용량 스레드 풀 FixThreadPool

newFixedThreadPool(int num), num은 지정하려는 스레드의 고정 개수입니다

	ExecutorService executorService = Executors.newFixedThreadPool(5);

      for (int i = 0; i < 10; i++) {
          final int finalI = i;
          executorService.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }

출력:

pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run>4
pool- 1-thread-4run>3
pool-1-thread-5run>5
pool-1-thread-3run>2
pool- 1- 스레드-3run>8
pool-1-thread-3run>9
pool-1-thread-2run>1
pool-1- thread- 175b3c38ff368dcafe01d2cfbd3d86ea2run>0
pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2run>7
pool-1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>6

볼 수 있습니다 스레드는 재사용을 위해 사용됩니다.

FixedThreadPool이 고정 스레드 풀인 이유는 무엇인가요?

소스 코드 분석

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
    }

이 소스 코드에서 코어 스레드 수(corePoolSize)와 최대 스레드 수(maximumPoolSize)가 모두 nThread임을 알 수 있습니다. 확장되고 스레드 수가 고정됩니다.

단일 스레드 풀 SingleThreadExecutor

	ExecutorService executorService = Executors.newSingleThreadExecutor();

      for (int i = 0; i < 10; i++) {
          final int finalI = i;
          executorService.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });

      }

SingleThreadExecutor에는 왜 스레드가 하나만 포함되어 있나요?

소스 코드 분석

public static ExecutorService newSingleThreadExecutor() {
        return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
    }

이 소스 코드를 보면 코어 스레드 수(corePoolSize)와 최대 스레드 수(maximumPoolSize)가 모두 1이므로 하나의 스레드만 포함되어 있음을 알 수 있습니다.

ScheduledThreadPool

	  int initDelay=10; //初始化延时
      int period=1;//初始化延迟过了之后,每秒的延时

      ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

      scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+"<thread->run>");
          }
      },initDelay,period, TimeUnit.SECONDS);

이 코드의 효과는 프로그램이 실행된 후 10초를 기다린 후 첫 번째 결과를 출력하고 1초마다 결과를 출력하는 것입니다.

ScheduledThreadPool이 권장되지 않는 이유는 무엇입니까?

소스 코드 분석

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
    }

ScheduledThreadPool의 최대 스레드 수(maximumPoolSize)는 Integer 2147483647의 최대값임을 알 수 있는데, 이는 무제한 스레드 생성과 동일하며, 스레드를 생성하려면 메모리가 필요하므로 메모리 오버플로가 발생합니다. , 그리고 일반적으로 머신은 그렇게 많은 수의 스레드를 생성하기 위해 그렇게 큰 메모리를 사용하지 않습니다.

ThreadPoolExecutor는 스레드 풀을 생성합니다(강력히 권장)

	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
              2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
              Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

      for (int i = 0; i < 12; i++) {
          final int finalI = i;
          threadPoolExecutor.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }
ThreadPoolExecutor
	public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        
    }
  • corePoolSize의 7개 매개변수에 대한 자세한 설명: 코어 스레드 수. 이러한 스레드는 일단 생성되면 소멸되지 않고 항상 존재합니다. 스레드 풀에는 기본적으로 스레드가 없습니다. 작업이 도착하면 ThreadFactory를 통해 스레드가 생성되고 항상 존재합니다.

  • maximumPoolSize: 최대 스레드 수입니다. 비코어 스레드 수 = maximumPoolSize-corePoolSize 비코어 스레드 수는 실제로 확장 가능한 스레드 수이며 삭제될 수 있습니다.

  • keepAliveTime: 비코어 스레드의 유휴 생존 시간입니다. 확장을 통해 생성된 비핵심 스레드 수가 keepAliveTime 이후에도 여전히 유휴 상태인 경우 이러한 비핵심 스레드는 삭제됩니다.

  • unit: keepAliveTime의 시간 단위, 예: 초

  • workQueue: 대기 영역. 작업 > corePoolSize가 오면 해당 작업은 workQueue의 차단 대기열에 저장되어 다른 스레드가 처리될 때까지 기다립니다.

  • threadFactory: 스레드 팩토리. 스레드를 생성하는 방법입니다.

  • 처리자: 거부 전략. > 최대 스레드 수 + workQueue의 용량이 되면 거부 정책이 실행됩니다

workQueue

ArrayBlockingQueue: 제한된 차단 대기열. 대기열에는 크기 제한이 있습니다. 용량을 초과하면 확장 또는 거부 정책이 트리거됩니다.

	public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

LinkedBlockingQueue: 무제한 차단 큐, 큐에는 크기 제한이 없으며 메모리 오버플로가 발생할 수 있습니다.

	 public LinkedBlockingQueue() {
        this(2147483647);
    }
handler

AbortPolicy: 직접 예외를 발생시킵니다.

	public static class AbortPolicy implements RejectedExecutionHandler {
        public AbortPolicy() {
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
        }
    }

DiscardPolicy: 작업이 수행되지 않습니다. 작업을 자동으로 삭제

	public static class DiscardPolicy implements RejectedExecutionHandler {
        public DiscardPolicy() {
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

DiscardOldestPolicy: 가장 긴 기존 작업을 삭제합니다

	public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        public DiscardOldestPolicy() {
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }

        }
    }

CallerRunsPolicy: 작업을 제출한 스레드가 작업을 처리하도록 합니다

	public static class CallerRunsPolicy implements RejectedExecutionHandler {
        public CallerRunsPolicy() {
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }

        }
    }

threadFactory

	ThreadFactory threadFactory = Executors.defaultThreadFactory();

      threadFactory.newThread(new Runnable() {
          @Override
          public void run() {
              System.out.println("threadFactory");
          }
      }).start();
如何触发拒绝策略和线程池扩容?
	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
              2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
              Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

      for (int i = 0; i < 26; i++) { //并发数26
          final int finalI = i;
          threadPoolExecutor.execute(new Runnable() {
              public void run() {
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }
      /**
       * 核心线程数=10,最大线程数=20,故可扩容线程数=20-10
       * BlockingQueue的大小为5,故等待区的大小为5,也就是当并发数<=核心线程数+5不会扩容,并发数大于16才会扩容
       *
       * 触发扩容:并发数>核心线程数+阻塞队列的大小
       * 对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容
       * 因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略
       */
  • 为什么这段代码会触发拒绝策略

对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容,但是又因为因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略。

  • 怎么触发扩容

触发扩容:并发数>核心线程数(corePoolSize)+阻塞队列(workQueue)的大小

  • 使用Java纯手写一个线程池

위 내용은 Java 스레드 풀을 만드는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제