> Java > java지도 시간 > 본문

Java 스레드 풀이란 무엇입니까? Java 스레드 풀에 대한 자세한 설명

不言
풀어 주다: 2018-09-19 15:13:35
원래의
2082명이 탐색했습니다.

이 기사에서는 Java 스레드 풀이 무엇인지 설명합니다. Java 스레드 풀에 대한 자세한 설명은 참고할만한 가치가 있습니다. 도움이 필요한 친구가 참조하면 도움이 될 것입니다.

1. 스레드 풀이란 무엇입니까?

java.util.concurrent.Executors는 스레드 풀을 생성하기 위한 java.util.concurrent.Executor 인터페이스 구현을 제공합니다.

멀티 스레딩 기술은 주로 다음 문제를 해결합니다. 프로세서 유닛의 다중 스레드 프로세서 유닛의 유휴 시간을 크게 줄이고 프로세서 유닛의 처리 용량을 늘릴 수 있습니다.​​

서버가 작업을 완료하는 데 필요한 시간은 T1이 스레드 시간을 생성하고, T2가 스레드에서 작업을 실행하고, T3가 스레드 시간을 파괴한다고 가정합니다.

T1 + T3이 T2보다 훨씬 큰 경우 스레드 풀을 사용하여 서버 성능을 향상시킬 수 있습니다.

스레드 풀에는 다음 네 가지 기본 구성 요소가 포함됩니다.

  • 1. 스레드 풀 관리자(ThreadPool): 스레드 풀 생성, 스레드 풀 삭제 및 새 작업 추가를 포함하여 스레드 풀을 생성하고 관리하는 데 사용됩니다.

  • 2. 작업자 스레드(PoolWorker): 작업이 없을 때 대기 상태에 있으며 작업을 주기적으로 실행할 수 있습니다.

  • 3. 작업 인터페이스(Task): 각 작업이 수행해야 하는 인터페이스입니다. 구현: 작업자 스레드가 작업 실행을 예약합니다. 주로 작업 시작, 작업 실행 후 작업 완료, 작업 실행 상태 등을 규정합니다.

  • 4. taskQueue): 처리되지 않은 작업을 저장하는 데 사용됩니다. 버퍼링 메커니즘을 제공합니다.

스레드 풀 기술은 T1 및 T3 시간을 단축하거나 조정하여 서버 프로그램의 성능을 향상시키는 방법에 중점을 둡니다. 서버 프로그램의 시작 및 종료 시간 또는 일부 유휴 시간에 각각 T1과 T3를 배치하므로 서버 프로그램이 고객 요청을 처리할 때 T1과 T3의 오버헤드가 발생하지 않습니다.

스레드 풀은 T1과 T3에서 생성되는 기간을 조정할 뿐만 아니라 생성되는 스레드 수도 크게 줄입니다. 예를 살펴보세요.

서버가 하루에 50,000개의 요청을 처리해야 한다고 가정해 보겠습니다. 각 요청에는 별도의 스레드에 완료가 필요합니다. 스레드 풀에서는 일반적으로 스레드 수가 고정되어 있으므로 생성되는 총 스레드 수는 스레드 풀의 스레드 수를 초과하지 않습니다. 스레드는 50,000개입니다. 일반적인 스레드 풀 크기는 50,000보다 훨씬 작습니다. 따라서 스레드 풀을 사용하는 서버 프로그램은 50,000개를 생성하기 위해 요청을 처리하는 데 시간을 낭비하지 않으므로 효율성이 향상됩니다.

2. 공통 스레드 풀

①newSingleThreadExecutor

단일 스레드 스레드 풀, 즉 스레드 풀에서는 한 번에 하나의 스레드만 작동하며 단일 스레드는 작업을 순차적으로 실행합니다.

②newFixedThreadExecutor(n )

고정 개수 스레드 풀, 작업이 제출되지 않으면 최대 스레드 풀 수에 도달할 때까지 스레드가 되며, 이후 실행을 계속하기 전에 이전 작업이 완료될 때까지 대기 대기열에 들어갑니다.

3newCacheThreadExecutor(권장)

스레드 풀을 캐시할 수 있습니다.

스레드 풀의 크기가 작업을 처리하는 데 필요한 스레드를 초과하는 경우 일부 유휴 스레드(일반적으로 60초 동안 실행되지 않음)가 재활용됩니다. 작업이 오면 실행을 위해 새 스레드가 지능적으로 추가됩니다.

④newScheduleThreadExecutor

무제한 크기의 스레드 풀, 예약 및 주기적 실행 스레드 지원

Java에서 제공하는 스레드 풀이 더 강력합니다. 스레드 풀의 작동 원리를 이해하면 그렇지 않을 것입니다. 클래스 라이브러리의 스레드 풀을 보면 이상하게 느껴질 것입니다.

Java 스레드 풀이란 무엇입니까? Java 스레드 풀에 대한 자세한 설명

Java 스레드 풀이란 무엇입니까? Java 스레드 풀에 대한 자세한 설명

기사 2:

Java 스레드 풀 사용 지침

소개

스레드 사용은 jdk1에서 매우 중요한 위치를 차지합니다. 4 이전 jdk 버전에서는 스레드 풀 사용이 매우 간단합니다. 이 상황은 jdk1.5 이후로 많이 바뀌었습니다. Jdk1.5 이후에는 java.util.concurrent 패키지가 추가되었습니다. 이 패키지는 주로 Java에서 스레드 및 스레드 풀의 사용을 소개합니다. 개발 중 스레드 문제를 처리하는 데 큰 도움이 되었습니다.

두 번째: 스레드 풀

스레드 풀의 역할:

스레드 풀의 기능은 시스템의 실행 스레드 수를 제한하는 것입니다.
 시스템 환경에 따라 스레드 수를 자동 또는 수동으로 설정하여 최상의 작업 효과를 얻을 수 있으며 시스템 리소스 낭비가 줄어들고 시스템 혼잡과 비효율이 발생합니다. 스레드 풀을 사용하여 스레드 수를 제어하고 다른 스레드는 대기열에 대기합니다. 작업이 실행된 후 대기열의 맨 앞에 있는 작업이 선택되고 실행이 시작됩니다. 대기열에 대기 중인 프로세스가 없으면 스레드 풀의 이 리소스가 대기 중입니다. 새 작업을 실행해야 할 때 스레드 풀에 대기 중인 작업자 스레드가 있으면 실행을 시작할 수 있습니다. 그렇지 않으면 대기 대기열에 들어갑니다.

스레드 풀을 사용하는 이유:

1. 스레드 생성 및 소멸 횟수를 줄이면 각 작업자 스레드를 재사용하고 여러 작업을 수행할 수 있습니다.

2. 과도한 메모리 소모로 인해 서버가 소진되는 것을 방지하기 위해 시스템 용량에 따라 스레드 풀에서 작동하는 스레드 수를 조정할 수 있습니다. (각 스레드에는 약 1MB의 메모리가 필요하며 더 많은 스레드가 열릴수록 더 많은 메모리가 소비되고 결국 충돌이 발생합니다.)

Java에서 스레드 풀의 최상위 인터페이스는 Executor이지만 엄밀히 말하면 Executor는 스레드 풀이 아니라 스레드를 실행하기 위한 도구일 뿐입니다. 실제 스레드 풀 인터페이스는 ExecutorService입니다.

더 중요한 클래스:

Classification Function
ExecutorService 실제 스레드 풀 인터페이스.
ScheduledExecutorService 은 Timer/TimerTask와 유사할 수 있으며 반복적인 작업 실행이 필요한 문제를 해결할 수 있습니다.
ThreadPoolExecutor ExecutorService의 기본 구현입니다.
ScheduledThreadPoolExecutor 주기적인 작업 예약의 클래스 구현인 ThreadPoolExecutor의 ScheduledExecutorService 인터페이스 구현을 상속합니다.

스레드 풀을 구성하는 것은 상대적으로 복잡합니다. 특히 스레드 풀의 원리가 명확하지 않은 경우에는 구성된 스레드 풀이 최적이 아닐 가능성이 매우 높으므로 Executors 클래스에 일부 정적 팩토리가 제공됩니다. . , 일반적으로 사용되는 스레드 풀을 생성합니다.

1. newSingleThreadExecutor

단일 스레드 풀을 생성합니다. 이 스레드 풀에는 단 하나의 스레드만 작동하며 이는 모든 작업을 순차적으로 실행하는 단일 스레드와 동일합니다. 유일한 스레드가 비정상적으로 종료되면 새 스레드가 이를 대체합니다. 이 스레드 풀은 모든 작업이 제출된 순서대로 실행되도록 보장합니다.

2.newFixedThreadPool

고정 크기 스레드 풀을 생성합니다. 스레드는 스레드 풀의 최대 크기에 도달할 때까지 작업이 제출될 때마다 생성됩니다. 스레드 풀의 크기는 최대값에 도달하면 변경되지 않습니다. 실행 예외로 인해 스레드가 종료되면 스레드 풀은 새 스레드로 채워집니다.

3. newCachedThreadPool

캐시 가능한 스레드 풀을 생성합니다. 스레드 풀의 크기가 작업을 처리하는 데 필요한 스레드

를 초과하면 일부 유휴 스레드(60초 동안 작업을 실행하지 않음)가 작업 수가 증가하면 이 스레드 풀이 재활용됩니다. 작업을 처리하기 위해 지능적으로 새 스레드를 추가할 수 있습니다. 이 스레드 풀은 스레드 풀의 크기를 제한하지 않습니다. 스레드 풀의 크기는 전적으로 운영 체제(또는 JVM)가 생성할 수 있는 최대 스레드 크기에 따라 달라집니다.

4.newScheduledThreadPool

무제한 크기의 스레드 풀을 만듭니다. 이 스레드 풀은 작업의 타이밍과 주기적인 실행을 지원합니다.

Instance

1: newSingleThreadExecutor

package com.thread;
 /* * 
  *通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class MyThread extends Thread { 

    @Override public void run() { // TODO Auto-generated method stub // super.run();
    System.out.println(Thread.currentThread().getName()+"正在执行....");
    } 
}
로그인 후 복사
rrree

결과: # 🎜🎜 #

package com.thread; 
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; 
/* 
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class singleThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newSingleThreadExecutor(); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
 Thread t1=new MyThread();

        Thread t2=new MyThread();

        Thread t3=new MyThread();

        Thread t4=new MyThread();

        Thread t5=new MyThread(); //将线程放到池中执行;
 pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5); //关闭线程池
 pool.shutdown();

    }

}
로그인 후 복사

2newFixedThreadPool

pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
로그인 후 복사
결과:

 package com.thread; 
import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors; 
/* * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class fixedThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newFixedThreadPool(2); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
 Thread t1=new MyThread();

        Thread t2=new MyThread();

        Thread t3=new MyThread();

        Thread t4=new MyThread();

        Thread t5=new MyThread(); //将线程放到池中执行;
 pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5); //关闭线程池
 pool.shutdown();

    }

}
로그인 후 복사
3,

newCachedThreadPool# 🎜🎜#

pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-2正在执行....
로그인 후 복사
결과:

package com.thread;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 /* 
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class cachedThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newCachedThreadPool(); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
 Thread t1=new MyThread();

        Thread t2=new MyThread();

        Thread t3=new MyThread();

        Thread t4=new MyThread();

        Thread t5=new MyThread(); //将线程放到池中执行;
 pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5); //关闭线程池
 pool.shutdown();

    }

}
로그인 후 복사

4,

newScheduledThreadPool

pool-1-thread-2正在执行....
pool-1-thread-1正在执行....
pool-1-thread-3正在执行....
pool-1-thread-4正在执行....
pool-1-thread-5正在执行....
로그인 후 복사
결과:

package com.thread; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; 
/* * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * http://blog.csdn.net/qq_31753145/article/details/50899119 * */

public class scheduledThreadExecutorTest{ public static void main(String[] args) { // TODO Auto-generated method stub
 ScheduledThreadPoolExecutor exec =new ScheduledThreadPoolExecutor(1);
       exec.scheduleAtFixedRate(new Runnable(){//每隔一段时间就触发异常
 @Override public void run() { // TODO Auto-generated method stub //throw new RuntimeException();
            System.out.println("===================");

        }}, 1000, 5000, TimeUnit.MILLISECONDS);  

       exec.scheduleAtFixedRate(new Runnable(){//每隔一段时间打印系统时间,证明两者是互不影响的
 @Override public void run() { // TODO Auto-generated method stub
 System.out.println(System.nanoTime());

        }}, 1000, 2000, TimeUnit.MILLISECONDS);

    }

}
로그인 후 복사

3: 스레드 수영장 실행자 자세한 설명

ThreadPoolExecutor의 전체 생성자의 서명은 다음과 같습니다.

ThreadPoolExecutor

(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit 단위, BlockingQueue<runnable> workQue ue, ThreadFactory threadFactory, RejectedExecutionHandler 핸들러)</runnable> .(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)</runnable> .

corePoolSize - 池中所保存的线程数,包括空闲线程。

maximumPoolSize-池中允许的最大线程数。

keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

unit - keepAliveTime 参数的时间单位。

workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。

threadFactory - 执行程序创建新线程时使用的工厂。

handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

ThreadPoolExecutor是Executors类的底层实现。

在JDK帮助文档中,有如此一段话:

“强烈建议程序员使用较为方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)Executors.newSingleThreadExecutor()corePoolSize

- 유휴 스레드를 포함하여 풀에 저장된 스레드 수입니다.

maximumPoolSize

- 풀에 허용되는 최대 스레드 수입니다.

keepAliveTime

- 스레드 수가 코어보다 큰 경우 초과 유휴 스레드가 종료되기 전에 새 작업을 기다리는 최대 시간입니다.

unit - keepAliveTime 매개변수의 시간 단위입니다.

workQueue - 실행 전에 작업을 보관하는 데 사용되는 대기열입니다. 이 큐에는 실행 메소드에 의해 제출된 실행 가능한 작업만 보관됩니다.

threadFactory

- 실행자가 새 스레드를 생성하는 데 사용하는 팩토리입니다.

handler

- 스레드 범위 및 큐 용량 초과로 인해 실행이 차단될 때 사용되는 핸들러입니다. #🎜🎜##🎜🎜##🎜🎜#ThreadPoolExecutor는 Executors 클래스의 기본 구현입니다. #🎜🎜##🎜🎜##🎜🎜#JDK 도움말 문서에는 다음과 같은 문구가 있습니다. #🎜🎜##🎜🎜# "프로그래머는 보다 편리한 Executors를 사용하는 것이 좋습니다. > 팩토리 메소드 Executors.newCachedThreadPool()(제한되지 않은 스레드 풀, 자동 스레드 재활용 수행 가능), Executors.newFixedThreadPool(int)(고정 크기 스레드 풀) Executors .newSingleThreadExecutor( ) (단일 배경 스레드) #🎜🎜##🎜🎜#모두 대부분의 사용 시나리오에 대해 사전 정의된 설정이 있습니다.”#🎜🎜##🎜🎜#아래에는 여러 클래스의 소스 코드가 소개되어 있습니다. #🎜 🎜##🎜🎜##🎜🎜#ExecutorService newFixedThreadPool(int nThreads): 고정 크기 스레드 풀. #🎜🎜##🎜🎜##🎜🎜#corePoolSize와 maximumPoolSize의 크기가 동일함을 알 수 있다(사실 나중에 소개하겠지만 무한한 큐를 사용하면 maximumPoolSize 매개변수는 의미가 없다). keepAliveTime 및 단위 설정 값 테이블의 이름은 무엇입니까? -살고 싶지 않다는 것을 깨달을 때입니다! 마지막 BlockingQueue는 LinkedBlockingQueue를 선택했는데, 이는 무제한이라는 특징을 가지고 있습니다. #🎜🎜#
===================
23119318857491
23121319071841
23123319007891
===================
23125318176937
23127318190359
===================
23129318176148
23131318344312
23133318465896
===================
23135319645812
로그인 후 복사
#🎜🎜##🎜🎜#ExecutorService newSingleThreadExecutor(): 단일 스레드 #🎜🎜##🎜🎜#
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>());   

      }</runnable>
로그인 후 복사
#🎜🎜##🎜🎜#ExecutorService newCachedThreadPool(): 무제한 스레드 풀, 자동 스레딩 수행 가능 재활용#🎜🎜##🎜🎜##🎜🎜#이 구현은 흥미롭습니다. 첫 번째는 무제한 스레드 풀이므로 maximumPoolSize가 크다는 것을 알 수 있습니다. 둘째, BlockingQueue 선택에는 동기 대기열이 사용됩니다. 이 BlockingQueue에 대해 조금 익숙하지 않을 수 있습니다. 간단히 말해서 이 QUEUE에서 각 삽입 작업은 다른 스레드의 해당 제거 작업을 기다려야 합니다. #🎜🎜#
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService   
                 (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>()));   
       }</runnable>
로그인 후 복사
#🎜🎜# BlockingQueue 매개변수부터 시작하겠습니다. JDK에서는 실제로 세 가지 유형의 대기열이 있다는 것이 매우 명확해졌습니다. #🎜🎜##🎜🎜#All BlockingQueue는 제출된 작업을 전송하고 보류하는 데 사용할 수 있습니다. 이 대기열은 풀 크기와 상호 작용하는 데 사용할 수 있습니다. #🎜🎜#

如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)

如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

queue上的三种类型。

排队有三种通用策略:

直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的 maximumPoolSizes时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

BlockingQueue的选择。

例子一:使用直接提交策略,也即SynchronousQueue。

首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。

我们使用一下参数构造ThreadPoolExecutor:

 new ThreadPoolExecutor( 2, 3, 30, TimeUnit.SECONDS, new  SynchronousQueue<runnable>(), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy());</runnable>
로그인 후 복사

当核心线程已经有2个正在运行.

  1. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则Executor始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。

  2. 又来了一个任务(B),且核心2个线程还没有忙完,OK,接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。

  3. 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。”,所以必然会新建一个线程来运行这个任务。

  4. 暂时还可以,但是如果这三个任务都还没完成,连续来了两个任务,第一个添加入queue中,后一个呢?queue中无法插入,而线程数达到了maximumPoolSize,所以只好执行异常策略了。

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)。对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

什么意思?如果你的任务A1,A2有内部关联,A1需要先运行,那么先提交A1,再提交A2,当使用SynchronousQueue我们可以保证,A1必定先被执行,在A1么有被执行前,A2不可能添加入queue中。

例子二:使用无界队列策略,即LinkedBlockingQueue

这个就拿newFixedThreadPool来说,根据前文提到的规则:

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。那么当任务继续增加,会发生什么呢?

如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。OK,此时任务变加入队列之中了,那什么时候才会添加新线程呢?

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。这里就很有意思了,可能会出现无法加入队列吗?不像SynchronousQueue那样有其自身的特点,对于无界队列来说,总是可以加入的(资源耗尽,当然另当别论)。换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。所以要防止任务疯长,比如任务运行的实行比较长,而添加任务的速度远远超过处理任务的时间,而且还不断增加,不一会儿就爆了。

例子三:有界队列,使用ArrayBlockingQueue。

这个是最为复杂的使用,所以JDK不推荐使用也有些道理。与上面的相比,最大的特点便是可以防止资源耗尽的情况发生。

举例来说,请看如下构造方法:

 new ThreadPoolExecutor( 2, 4, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<runnable>(2), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy());</runnable>
로그인 후 복사

假设,所有的任务都永远无法执行完。

对于首先来的A,B来说直接运行,接下来,如果来了C,D,他们会被放到queue中,如果接下来再来E,F,则增加线程运行E,F。但是如果再来任务,队列无法再接受了,线程数也到达最大的限制了,所以就会使用拒绝策略来处理。

keepAliveTime

jdk中的解释是:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

有点拗口,其实这个不难理解,在使用了“池”的应用中,大多都有类似的参数需要配置。比如数据库连接池,DBCP中的maxIdle,minIdle参数。

什么意思?接着上面的解释,后来向老板派来的工人始终是“借来的”,俗话说“有借就有还”,但这里的问题就是什么时候还了,如果借来的工人刚完成一个任务就还回去,后来发现任务还有,那岂不是又要去借?这一来一往,老板肯定头也大死了。

合理的策略:既然借了,那就多借一会儿。直到“某一段”时间后,发现再也用不到这些工人时,便可以还回去了。这里的某一段时间便是keepAliveTime的含义,TimeUnit为keepAliveTime值的度量。

RejectedExecutionHandler

另一种情况便是,即使向老板借了工人,但是任务还是继续过来,还是忙不过来,这时整个队伍只好拒绝接受了。

RejectedExecutionHandler接口提供了对于拒绝任务的处理的自定方法的机会。在ThreadPoolExecutor中已经默认包含了4中策略,因为源码非常简单,这里直接贴出来。

CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

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

               r.run();

           }

       }
로그인 후 복사

这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。

AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException

 这种策略直接抛出异常,丢弃任务。

DiscardPolicy:不能执行的任务将被删除

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

       }
로그인 후 복사

 这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。

DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

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

               e.getQueue().poll();

               e.execute(r);

           }

       }
로그인 후 복사

该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。这个策略需要适当小心。

设想:如果其他线程都还在运行,那么新来任务踢掉旧任务,缓存在queue中,再来一个任务又会踢掉queue中最老任务。

总结:

keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。

反之,如果核心数较小,有界BlockingQueue数值又较小,同时keepAliveTime又设的很小,如果任务频繁,那么系统就会频繁的申请回收线程。

public static ExecutorService newFixedThreadPool(int nThreads) {
 return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>());
}</runnable>
로그인 후 복사

위 내용은 Java 스레드 풀이란 무엇입니까? Java 스레드 풀에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!