xiaosen_javashare:マイクロチャンネル公衆番号へようこそ注意
前回の記事で「@Async非同期タスクを使用して春ブーツ」 @Asyncを使用する非同期タスクの我々の理解では、この記事では、我々はスレッドプールを使用して、非同期タスクのスレッドを作成する方法を学習します。
「アリババJava開発者のためのハンドブック」内のスレッドの使用のために、次の要件があります。
次に、私たちはただ、スレッドプールを見てみましょう。
スレッドプールブリーフ
エグゼキュータJava5は、フレーム内に組み込まれています。
ThreadPoolExecutor解析スレッドプール
以下のクラス図:
図は、ThreadPoolExecutorコンストラクタです。
ここでは、最も完全な方法である設定パラメータのほとんどを参照してください。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 || maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ? null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize(基本的なスレッドプールのサイズ):スレッドプールにタスクを提出する際、スレッドプールは他のフリーの基本的なスレッドが新しいタスクを実行することができた場合でも、タスクを実行するスレッドを作成します、あなたが実行する必要のあるタスクの数がより大きくなるまで、スレッドを作成することになりますスレッドプールのサイズを作成することはもはや必要不可欠ではありません。あなたは、メソッドprestartAllCoreThreadsスレッドプールを呼び出す場合、スレッドプールは、事前に作成され、すべての基本的なスレッドを開始しています。
- maximumPoolSize(最大スレッドプールサイズ):スレッドプールを使用すると、スレッドの最大数を作成することができます。キューがいっぱいになると、スレッドの最大数よりも少ないが作成されているスレッドの数と、スレッド・プールには、タスクを実行するために、新しいスレッドを再作成します。それはあなたが効果なしに無制限のタスクキューにこのパラメータを使用する場合は、ことは注目に値します。
- keepAliveTimeが(スレッドアクティビティのホールド時間):スレッドプールのワーカースレッドが時間を生きて維持、アイドル状態の後。タスクの多くは、各タスクの実行時間が比較的短いのであれば、それは、この時間を上げるスレッドの利用率を向上させることができます。
- TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
- workQueue (任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。
-
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
-
ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情,比如设置daemon和优先级等等
-
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
-
- AbortPolicy:直接抛出异常。
- CallerRunsPolicy:只用调用者所在线程来运行任务。
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- DiscardPolicy:不处理,丢弃掉。
- 也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
线程池的工作方式
- 如果运行的线程少于 corePoolSize,则 Executor 始终创建新的线程,而不添加到queue中。
- 如果运行的线程等于或多于 corePoolSize,则 Executor 始终将请求加入队列,而不创建新的线程。
- 如果无法将请求加入队列(队列已满),则创建新的线程,除非创建此线程超出 maximumPoolSize,如果超过,在这种情况下,新的任务将被拒绝。
Executors提供的线程池方法
- newFixedThreadPool固定线程数的线程。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
从上面源代码可以看出新创建的newFixedThreadPool的corePoolSize和maximumPoolSize都被设置为nThreads。
说明:
- 如果当前运行的线程数小于corePoolSize,则创建新的线程来执行任务;
- 当前运行的线程数等于corePoolSize后,将任务加入LinkedBlockingQueue;
- 线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue中获取任务来执行。
FixedThreadPool使用无界队列 LinkedBlockingQueue(队列的容量为Intger.MAX_VALUE)作为线程池的工作队列会对线程池带来如下影响:
- 当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中的线程数不会超过corePoolSize;
- 使用无界队列时maximumPoolSize和keepAliveTime将是无效参数;
- 运行中的newFixedThreadPool(未执行shutdown()或shutdownNow()方法)不会拒绝任务。
- newSingleThreadExecutor单个线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
从上面源代码可以看出新创建的SingleThreadExecutor的corePoolSize和maximumPoolSize都被设置为1.其他参数和newFixedThreadPool相同。也是用的是无界队列存放。
说明:
- 如果当前运行的线程数少于corePoolSize,则创建一个新的线程执行任务;
- 当前线程池中有一个运行的线程后,将任务加入LinkedBlockingQueue
- 线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue中获取任务来执行。
- newCachedThreadPool, 先看一下源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
newCachedThreadPool的corePoolSize是0,maximumPoolSize被设置为Integer.MAX.VALUE。
说明:
- SynchronousQueue是无界的,在某次添加元素后必须等待其他线程取走后才能继续添加。
- 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。
- ScheduledExecutorService,此线程池支持定时以及周期性执行任务的需求。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
我们再看一下ScheduledThreadPoolExecutor中的源码:
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
调用schedule方法,delay是延迟多少时间执行。
以上就是Executors提供的几种常见的线程池的解析。
ThreadPoolTaskExecutor
下面我们来看一下spring为我们提供的线程池ThreadPoolTaskExecutor。下图是其类关系图:
private final Object poolSizeMonitor = new Object();
private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private int queueCapacity = Integer.MAX_VALUE;
private boolean allowCoreThreadTimeOut = false;
@Nullable
private TaskDecorator taskDecorator;
@Nullable
private ThreadPoolExecutor threadPoolExecutor;
这是ThreadPoolTaskExecutor类中的属性,可以看出依然需要ThreadPoolExecutor类来支持。默认使用无界队列。
在初始化方法中调用了ThreadPoolExecutor的构造器:
@Override
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
if (this.taskDecorator != null) {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler) {
@Override
public void execute(Runnable command) {
Runnable decorated = taskDecorator.decorate(command);
if (decorated != command) {
decoratedTaskMap.put(decorated, command);
}
super.execute(decorated);
}
};
}
else {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
}
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
说明:
- ライン市(threadPoolTaskExecutor)Spring構成では春ですので)最初に使用した後、それがcorePoolSizeアイドルスレッドのまま、注入され、それが唯一のkeepAliveSeconds時間後に解放過剰アイドルスレッドを意志作成し、市は(シャットダウンを呼び出すことはできませんラインスレッドプールが閉鎖されたため、それ以外の場合は、再び呼び出されるメソッドは、文句を言うでしょう。
- threadPoolTaskExecutorマルチタスク間の競合を防止するために、輪郭線の都市の複数に配置され、又はラインが異なるタスク等に使用される異なるサイズの都市に起因してもよいです。
概要
スレッドプールのJavaを簡単に紹介よりも、自分の長所と短所を提供する4つのスレッドプールと春が付属して、実際のプロジェクトでは、ニーズに応じて選択することができます。
国民の関心番号へようこそ。