文档库 最新最全的文档下载
当前位置:文档库 › 线程池应用

线程池应用

线程池应用
线程池应用

T hreadPoolExecutor

简介

一、线程池类为java.util.concurrent.ThreadPoolExecutor,常用构造方法为:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue workQueue,

RejectedExecutionHandler handler)

corePoolSize:线程池维护线程的最少数量

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime:线程池维护线程所允许的空闲时间

unit:线程池维护线程所允许的空闲时间的单位

workQueue:线程池所使用的缓冲队列

handler:线程池对拒绝任务的处理策略

一个任务通过execute(Runnable)方法被添加到线程池,任务就是一个Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时:

l 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

l 如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。

l 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

l 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

l 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:

NANOSECONDS、

MICROSECONDS、

MILLISECONDS、

SECONDS。

workQueue常用的是:java.util.concurrent.ArrayBlockingQueue

handler有四个选择:

ThreadPoolExecutor.AbortPolicy()

抛出java.util.concurrent.RejectedExecutionException异常

ThreadPoolExecutor.CallerRunsPolicy()

重试添加当前的任务,他会自动重复调用execute()方法

ThreadPoolExecutor.DiscardOldestPolicy()

抛弃旧的任务

ThreadPoolExecutor.DiscardPolicy()

抛弃当前的任务

二、相关参考

一个ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用Executors 工厂方法配置。

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。

为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的Executors 工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和

Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。否则,在手动配置和调整此类时,使用以下指导:

核心和最大池大小

ThreadPoolExecutor 将根据corePoolSize(参见getCorePoolSize())和maximumPoolSize(参见getMaximumPoolSize())设置的边界自动调整池大小。当新任务在方法execute(https://www.wendangku.net/doc/124433186.html,ng.Runnable) 中提交时,如果运行的线程少于corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于corePoolSize 而少于maximumPoolSize,则仅当队列满时才创建新线程。如果设置的corePoolSize 和maximumPoolSize 相同,则创建了固定大小的线程池。如果将maximumPoolSize 设置为基本的无界值(如Integer.MAX_V ALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用setCorePoolSize(int) 和setMaximumPoolSize(int) 进行动态更改。

按需构造

默认情况下,即使核心线程最初只是在新任务需要时才创建和启动的,也可以使用方法prestartCoreThread() 或prestartAllCoreThreads() 对其进行动态重写。

创建新线程

使用ThreadFactory 创建新线程。如果没有另外说明,则在同一个ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从newThread 返回null 时ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。

保持活动时间

如果池中当前有多于corePoolSize 的线程,则这些多出的线程在空闲时间超过keepAliveTime 时将会终止(参见getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法setKeepAliveTime(long, java.util.concurrent.TimeUnit) 动态地更改此参数。使用Long.MAX_V ALUE TimeUnit.NANOSECONDS 的值在关闭前有效地从以前的终止状态禁用空闲线程。

排队

所有BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

A. 如果运行的线程少于corePoolSize,则Executor 始终首选添加新的线程,而不进行排队。

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

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

排队有三种通用策略:

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

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

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

被拒绝的任务

当Executor 已经关闭,并且Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法execute(https://www.wendangku.net/doc/124433186.html,ng.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,

execute 方法都将调用其RejectedExecutionHandler 的RejectedExecutionHandler.rejectedExecution(https://www.wendangku.net/doc/124433186.html,ng.Runnable,

java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:

A. 在默认的ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时RejectedExecutionException。

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

C. 在ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。

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

定义和使用其他种类的RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

挂钩方法

此类提供protected 可重写的beforeExecute(https://www.wendangku.net/doc/124433186.html,ng.Thread, https://www.wendangku.net/doc/124433186.html,ng.Runnable) 和afterExecute(https://www.wendangku.net/doc/124433186.html,ng.Runnable, https://www.wendangku.net/doc/124433186.html,ng.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法terminated() 来执行Executor 完全终止后需要完成的所有特殊处理。

如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。

队列维护

方法getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(https://www.wendangku.net/doc/124433186.html,ng.Runnable) 和purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。

一、例子

创建TestThreadPool 类:

view plaincopy to clipboardprint?

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

public class TestThreadPool {

private static int produceTaskSleepTime = 2;

private static int produceTaskMaxNumber = 10;

public static void main(String[] args) {

// 构造一个线程池

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,

TimeUnit.SECONDS, new ArrayBlockingQueue(3),

new ThreadPoolExecutor.DiscardOldestPolicy());

for (int i = 1; i <= produceTaskMaxNumber; i++) {

try {

String task = "task@ " + i;

System.out.println("创建任务并提交到线程池中:" + task);

threadPool.execute(new ThreadPoolTask(task));

Thread.sleep(produceTaskSleepTime);

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

view plaincopy to clipboardprint?

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

public class TestThreadPool {

private static int produceTaskSleepTime = 2;

private static int produceTaskMaxNumber = 10;

public static void main(String[] args) {

// 构造一个线程池

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,

TimeUnit.SECONDS, new ArrayBlockingQueue(3),

new ThreadPoolExecutor.DiscardOldestPolicy());

for (int i = 1; i <= produceTaskMaxNumber; i++) {

try {

String task = "task@ " + i;

System.out.println("创建任务并提交到线程池中:" + task);

threadPool.execute(new ThreadPoolTask(task));

Thread.sleep(produceTaskSleepTime);

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

public class TestThreadPool {

private static int produceTaskSleepTime = 2;

private static int produceTaskMaxNumber = 10;

public static void main(String[] args) {

// 构造一个线程池

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,

TimeUnit.SECONDS, new ArrayBlockingQueue(3),

new ThreadPoolExecutor.DiscardOldestPolicy());

for (int i = 1; i <= produceTaskMaxNumber; i++) {

try {

String task = "task@ " + i;

System.out.println("创建任务并提交到线程池中:" + task);

threadPool.execute(new ThreadPoolTask(task));

Thread.sleep(produceTaskSleepTime);

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

创建ThreadPoolTask类:

view plaincopy to clipboardprint?

import java.io.Serializable;

public class ThreadPoolTask implements Runnable, Serializable {

private Object attachData;

ThreadPoolTask(Object tasks) {

this.attachData = tasks;

}

public void run() {

System.out.println("开始执行任务:" + attachData);

attachData = null;

}

public Object getTask() {

return this.attachData;

}

}

view plaincopy to clipboardprint?

import java.io.Serializable;

public class ThreadPoolTask implements Runnable, Serializable {

private Object attachData;

ThreadPoolTask(Object tasks) {

this.attachData = tasks;

}

public void run() {

System.out.println("开始执行任务:" + attachData);

attachData = null;

}

public Object getTask() {

return this.attachData;

}

}

JAVA线程池原理333

在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.将需处理的任务的数量大 使用线程池的好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。 线程池工作原理:

线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。 线程池的替代方案 线程池远不是服务器应用程序内使用多线程的唯一方法。如同上面所提到的,有时,为每个新任务生成一个新线程是十分明智的。然而,如果任务创建过于频繁而任务的平均处理时间过短,那么为每个任务生成一个新线程将会导致性能问题。 另一个常见的线程模型是为某一类型的任务分配一个后台线程与任务队列。AWT 和 Swing 就使用这个模型,在这个模型中有一个 GUI 事件线程,导致用户界面发生变化的所有工作都必须在该线程中执行。然而,由于只有一个 AWT 线程,因此要在 AWT 线程中执行任务可能要花费相当长时间才能完成,这是不可取的。因此,Swing 应用程序经常需要额外的工作线程,用于运行时间很长的、同 UI 有关的任务。 每个任务对应一个线程方法和单个后台线程(single-background-thread)方法在某些情形下都工作得非常理想。每个任务一个线程方法在只有少量运行时间很长的任务时工作得十分好。而只要调度可预见性不是很重要,则单个后台线程方法就工作得十分好,如低优先级后台任务就是这种情况。然而,大多数服务器应用程序都是面向处理大量的短期任务或子任务,因此往往希望具有一种能够以低开销有效地处理这些任务的机制以及一些资源管理和定时可预见性的措施。线程池提供了这些优点。 工作队列 就线程池的实际实现方式而言,术语“线程池”有些使人误解,因为线程池“明显的”实现在大多数情形下并不一定产生我们希望的结果。术语“线程池”先于Java 平台出现,因此它可能是较少面向对象方法的产物。然而,该术语仍继续广泛应用着。 虽然我们可以轻易地实现一个线程池类,其中客户机类等待一个可用线程、将任务传递给该线程以便执行、然后在任务完成时将线程归还给池,但这种方法却存在几个潜在的负面影响。例如在池为空时,会发生什么呢?试图向池线程传递任务的调用者都会发现池为空,在调用者等待一个可用的池线程时,它的线程将阻塞。我们之所以要使用后台线程的原因之一常常是为了防止正在提交的线程被阻塞。完全堵住调用者,如在线程池的“明显的”实现的情况,可以杜绝我们试图解决的问题的发生。 我们通常想要的是同一组固定的工作线程相结合的工作队列,它使用 wait() 和

解决多线程中11个常见问题

并发危险 解决多线程代码中的11 个常见的问题 Joe Duffy 本文将介绍以下内容:?基本并发概念 ?并发问题和抑制措施 ?实现安全性的模式?横切概念本文使用了以下技术: 多线程、.NET Framework 目录 数据争用 忘记同步 粒度错误 读写撕裂 无锁定重新排序 重新进入 死锁 锁保护 戳记 两步舞曲 优先级反转 实现安全性的模式 不变性 纯度 隔离 并发现象无处不在。服务器端程序长久以来都必须负责处理基本并发编程模型,而随着多核处理器的日益普及,客户端程序也将需要执行一些任务。随着并发操作的不断增加,有关确保安全的问题也浮现出来。也就是说,在面对大量逻辑并发操作和不断变化的物理硬件并行性程度时,程序必须继续保持同样级别的稳定性和可靠性。 与对应的顺序代码相比,正确设计的并发代码还必须遵循一些额外的规则。对内存的读写以及对共享资源的访问必须使用同步机制进行管制,以防发生冲突。另外,通常有必要对线程进行协调以协同完成某项工作。 这些附加要求所产生的直接结果是,可以从根本上确保线程始终保持一致并且保证其顺利向前推进。同步和协调对时间的依赖性很强,这就导致了它们具有不确定性,难于进行预测和测试。 这些属性之所以让人觉得有些困难,只是因为人们的思路还未转变过来。没有可供学习的专门API,也没有可进行复制和粘贴的代码段。实际上的确有一组基础概念需要您学习和适应。很可能随着时间的推移某些语言和库会隐藏一些概念,但如果您现在就开始执行并发操作,则不会遇到这种情况。本

文将介绍需要注意的一些较为常见的挑战,并针对您在软件中如何运用它们给出一些建议。 首先我将讨论在并发程序中经常会出错的一类问题。我把它们称为“安全隐患”,因为它们很容易发现并且后果通常比较严重。这些危险会导致您的程序因崩溃或内存问题而中断。 当从多个线程并发访问数据时会发生数据争用(或竞争条件)。特别是,在一个或多个线程写入一段数据的同时,如果有一个或多个线程也在读取这段数据,则会发生这种情况。之所以会出现这种问题,是因为Windows 程序(如C++ 和Microsoft .NET Framework 之类的程序)基本上都基于共享内存概念,进程中的所有线程均可访问驻留在同一虚拟地址空间中的数据。静态变量和堆分配可用于共享。请考虑下面这个典型的例子: static class Counter { internal static int s_curr = 0; internal static int GetNext() { return s_curr++; } } Counter 的目标可能是想为GetNext 的每个调用分发一个新的唯一数字。但是,如果程序中的两个线程同时调用GetNext,则这两个线程可能被赋予相同的数字。原因是s_curr++ 编译包括三个独立的步骤: 1.将当前值从共享的s_curr 变量读入处理器寄存器。 2.递增该寄存器。 3.将寄存器值重新写入共享s_curr 变量。 按照这种顺序执行的两个线程可能会在本地从s_curr 读取了相同的值(比如42)并将其递增到某个值(比如43),然后发布相同的结果值。这样一来,GetNext 将为这两个线程返回相同的数字,导致算法中断。虽然简单语句s_curr++ 看似不可分割,但实际却并非如此。 忘记同步 这是最简单的一种数据争用情况:同步被完全遗忘。这种争用很少有良性的情况,也就是说虽然它们是正确的,但大部分都是因为这种正确性的根基存在问题。 这种问题通常不是很明显。例如,某个对象可能是某个大型复杂对象图表的一部分,而该图表恰好可使用静态变量访问,或在创建新线程或将工作排入线程池时通过将某个对象作为闭包的一部分进行传递可变为共享图表。 当对象(图表)从私有变为共享时,一定要多加注意。这称为发布,在后面的隔离上下文中会对此加以讨论。反之称为私有化,即对象(图表)再次从共享变为私有。 对这种问题的解决方案是添加正确的同步。在计数器示例中,我可以使用简单的联锁: static class Counter { internal static volatile int s_curr = 0; internal static int GetNext() { return Interlocked.Increment(ref s_curr);

java深入理解线程池

深入研究线程池 一.什么是线程池? 线程池就是以一个或多个线程[循环执行]多个应用逻辑的线程集合. 注意这里用了线程集合的概念是我生造的,目的是为了区分执行一批应用逻辑的多个线程和 线程组的区别.关于线程组的概念请参阅基础部分. 一般而言,线程池有以下几个部分: 1.完成主要任务的一个或多个线程. 2.用于调度管理的管理线程. 3.要求执行的任务队列. 那么如果一个线程循环执行一段代码是否是线程池? 如果极端而言,应该算,但实际上循环代码应该算上一个逻辑单元.我们说最最弱化的线程池 应该是循环执行多个逻辑单元.也就是有一批要执行的任务,这些任务被独立为多个不同的执行单元.比如: int x = 0; while(true){ x ++; } 这就不能说循环中执行多个逻辑单元,因为它只是简单地对循环外部的初始变量执行++操作. 而如果已经有一个队列 ArrayList al = new ArrayList(); for(int i=0;i<10000;i++){ al.add(new AClass()); } 然后在一个线程中执行: while(al.size() != 0){ AClass a = (AClass)al.remove(0); a.businessMethod(); } 我们说这个线程就是循环执行多个逻辑单元.可以说这个线程是弱化的线程池.我们习惯上把这些相对独立的逻辑单元称为任务. 二.为什么要创建线程池? 线程池属于对象池.所有对象池都具有一个非常重要的共性,就是为了最大程度复用对象.那么 线程池的最重要的特征也就是最大程度利用线程. 从编程模型模型上说讲,在处理多任务时,每个任务一个线程是非常好的模型.如果确实可以这么做我们将可以使用编程模型更清楚,更优化.但是在实际应用中,每个任务一个线程会使用系统限入"过度切换"和"过度开销"的泥潭. 打个比方,如果可能,生活中每个人一辆房车,上面有休息,娱乐,餐饮等生活措施.而且道路交道永远不堵车,那是多么美好的梦中王国啊.可是残酷的现实告诉我们,那是不可能的.不仅每个人一辆车需要无数多的社会资源,而且地球上所能容纳的车辆总数是有限制的. 首先,创建线程本身需要额外(相对于执行任务而必须的资源)的开销.

Java大数据培训技术分享:为什么用线程池及常见四种线程池

Java大数据培训技术分享:为什么用线程池及常见四种线程池 一、为什么用线程池 1、创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 >例如:> >记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3> >如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!> >正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销 2、线程并发数量过多,抢占系统资源从而导致阻塞 >我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况> >运用线程池能有效的控制线程最大并发数,避免以上的问题 3、对线程进行一些简单的管理 > 比如:延时执行、定时循环执行的策略等> > 运用线程池都能进行很好的实现 二、线程池 ThreadPoolExecutor 在 Java 中,线程池的概念是 Executor 这个接口,具体实现为ThreadPoolExecutor 类,学习 Java 中的线程池,就可以直接学习他了 对线程池的配置,就是对 ThreadPoolExecutor 构造函数的参数的配置,既然这些参数这么重要,就来看看构造函数的各个参数吧 ThreadPoolExecutor提供了四个构造函数

//五个参数的构造函数 public ThreadPoolExecutor(int corePoolSize, intmaximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue) //六个参数的构造函数 -1public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) //六个参数的构造函数 -2public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) //七个参数的构造函数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,

完成端口加线程池技术实现

WinSock 异步I/O模型[5]---完成端口 - Completion Port ---------------------------------------------------------------------------- 如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。Windows操作系统提供了五种I/O模型,分别是: ■选择(select); ■异步选择(WSAAsyncSelect); ■事件选择(WSAEventSelect); ■重叠I/O(Overlapped I/O); ■完成端口(Completion Port) 。 每一种模型适用于一种特定的应用场景。程序员应该对自己的应用需求非常明确,综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。 ============================================== █“完成端口”模型是迄今为止最复杂的一种 I/O 模型。但是,若一个应用程序同时需要管理很多的套接字, 那么采用这种模型,往往可以达到最佳的系统性能!但缺点是,该模型只适用于Windows NT 和 Windows 2000 以上版本的操作系统。 █因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多, 应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型。 █从本质上说,完成端口模型要求我们创建一个 Win32 完成端口对象,通过指定数量的线程, 对重叠 I/O 请求进行管理,以便为已经完成的重叠 I/O 请求提供服务。

Tomcat线程池实现简介

Tomcat线程池实现简介 目前市场上常用的开源Java Web容器有Tomcat、Resin和Jetty。其中Resin从V3.0后需要购买才能用于商业目的,而其他两种则是纯开源的。可以分别从他们的网站上下载最新的二进制包和源代码。 作为Web容器,需要承受较高的访问量,能够同时响应不同用户的请求,能够在恶劣环境下保持较高的稳定性和健壮性。在HTTP服务器领域,Apache HTTPD的效率是最高的,也是最为稳定的,但它只能处理静态页面的请求,如果需要支持动态页面请求,则必须安装相应的插件,比如mod_perl可以处理Perl脚本,mod_python可以处理Python脚本。 上面介绍的三中Web容器,都是使用Java编写的HTTP服务器,当然他们都可以嵌到Apache 中使用,也可以独立使用。分析它们处理客户请求的方法有助于了解Java多线程和线程池的实现方法,为设计强大的多线程服务器打好基础。 Tomcat是使用最广的Java Web容器,功能强大,可扩展性强。最新版本的Tomcat(5.5.17)为了提高响应速度和效率,使用了Apache Portable Runtime(APR)作为最底层,使用了APR 中包含Socket、缓冲池等多种技术,性能也提高了。APR也是Apache HTTPD的最底层。可想而知,同属于ASF(Apache Software Foundation)中的成员,互补互用的情况还是很多的,虽然使用了不同的开发语言。 Tomcat 的线程池位于tomcat-util.jar文件中,包含了两种线程池方案。方案一:使用APR 的Pool技术,使用了JNI;方案二:使用Java实现的ThreadPool。这里介绍的是第二种。如果想了解APR的Pool技术,可以查看APR的源代码。 ThreadPool默认创建了5个线程,保存在一个200维的线程数组中,创建时就启动了这些线程,当然在没有请求时,它们都处理“等待”状态(其实就是一个while循环,不停的等待notify)。如果有请求时,空闲线程会被唤醒执行用户的请求。 具体的请求过程是:服务启动时,创建一个一维线程数组(maxThread=200个),并创建空闲线程(minSpareThreads=5个)随时等待用户请求。当有用户请求时,调用threadpool.runIt(ThreadPoolRunnable)方法,将一个需要执行的实例传给ThreadPool中。其中用户需要执行的实例必须实现ThreadPoolRunnable接口。ThreadPool 首先查找空闲的线程,如果有则用它运行要执行ThreadPoolRunnable;如果没有空闲线程并且没有超过maxThreads,就一次性创建minSpareThreads个空闲线程;如果已经超过了maxThreads了,就等待空闲线程了。总之,要找到空闲的线程,以便用它执行实例。找到后,将该线程从线程数组中移走。接着唤醒已经找到的空闲线程,用它运行执行实例(ThreadPoolRunnable)。运行完ThreadPoolRunnable后,就将该线程重新放到线程数组中,作为空闲线程供后续使用。 由此可以看出,Tomcat的线程池实现是比较简单的,ThreadPool.java也只有840行代码。用一个一维数组保存空闲的线程,每次以一个较小步伐(5个)创建空闲线程并放到线程池中。使用时从数组中移走空闲的线程,用完后,再“归还”给线程池。ThreadPool提供的仅仅是线程池的实现,而如何使用线程池也是有很大学问的。让我们看看Tomcat是如何使用ThreadPool的吧。 Tomcat有两种EndPoint,分别是AprEndpoint和PoolTcpEndpoint。前者自己实现了一套线

操作系统精髓与设计原理课后答案

操作系统精髓与设计原理课后答案 第1章计算机系统概述 1.1列出并简要地定义计算机的四个主要组成部分。 主存储器,存储数据和程序;算术逻辑单元,能处理二进制数据;控制单元,解读存储器中的指令并且使他们得到执行;输入/输出设备,由控制单元管理。 1.2定义处理器寄存器的两种主要类别。 用户可见寄存器:优先使用这些寄存器,可以使机器语言或者汇编语言的程序员减少对主存储器的访问次数。对高级语言而言,由优化编译器负责决定把哪些变量应该分配给主存储器。一些高级语言,如C语言,允许程序言建议编译器把哪些变量保存在寄存器中。 控制和状态寄存器:用以控制处理器的操作,且主要被具有特权的操作系统例程使用,以控制程序的执行。 1.3一般而言,一条机器指令能指定的四种不同操作是什么? 处理器-寄存器:数据可以从处理器传送到存储器,或者从存储器传送到处理器。 处理器-I/O:通过处理器和I/O模块间的数据传送,数据可以输出到外部设备,或者从外部设备输入数据。 数据处理:处理器可以执行很多关于数据的算术操作或逻辑操作。 控制:某些指令可以改变执行顺序。 1.4什么是中断? 中断:其他模块(I/O,存储器)中断处理器正常处理过程的机制。 1.5多中断的处理方式是什么? 处理多中断有两种方法。第一种方法是当正在处理一个中断时,禁止再发生中断。第二种方法是定义中断优先级,允许高优先级的中断打断低优先级的中断处理器的运行。 1.6内存层次的各个元素间的特征是什么? 存储器的三个重要特性是:价格,容量和访问时间。 1.7什么是高速缓冲存储器? 高速缓冲存储器是比主存小而快的存储器,用以协调主存跟处理器,作为最近储存地址的缓冲区。1.8列出并简要地定义I/O操作的三种技术。 可编程I/O:当处理器正在执行程序并遇到与I/O相关的指令时,它给相应的I/O模块发布命令(用以执行这个指令);在进一步的动作之前,处理器处于繁忙的等待中,直到该操作已经完成。 中断驱动I/O:当处理器正在执行程序并遇到与I/O相关的指令时,它给相应的I/O模块发布命令,并继续执行后续指令,直到后者完成,它将被I/O模块中断。如果它对于进程等待I/O的完成来说是不必要的,可能是由于后续指令处于相同的进程中。否则,此进程在中断之前将被挂起,其他工作将被执行。 直接存储访问:DMA模块控制主存与I/O模块间的数据交换。处理器向DMA模块发送一个传送数据块的请求,(处理器)只有当整个数据块传送完毕后才会被中断。 1.9空间局部性和临时局部性间的区别是什么? 空间局部性是指最近被访问的元素的周围的元素在不久的将来可能会被访问。临时局部性(即时间局部性)是指最近被访问的元素在不久的将来可能会被再次访问。 1.10开发空间局部性和时间局部性的策略是什么? 空间局部性的开发是利用更大的缓冲块并且在存储器控制逻辑中加入预处理机制。时间局部性的开发是利用在高速缓冲存储器中保留最近使用的指令及数据,并且定义缓冲存储的优先级。 第2章操作系统概述

普元EOS工作流引擎设计原理

EOS工作流引擎工作原理 作者:Gocom注册用户dogreet(李国生) 1. 工作流基础知识 ……略 2. EOS工作流引擎工作原理 本文是我在工作之余写的一点我对EOS工作流的了解,我的理解不一定全是对的,可能会与引擎的真正的面目有出入。所以只能提供给大家一点参考。 2.1. EOS工作流引擎核心调度算法 EOS工作流最重要的组成部分是它的核心调度算法,在我们没有深入研究它的工作原理之前我们认为它的工作原理是在工作项,活动和流程实例对象上加了一些标志位来驱动流程的运转。认为其引擎完全是个由数据库来驱动流程的引擎(安徽二期的工作流平台好象就是以库表来驱动流程的运转),其实它是由事件来驱动流程运转的引擎,数据库只是把引擎运转前后的状态持久化。在我近来在工作之余对其引擎的工作原理进行跟踪才弄明白在EOS帮助文档上介绍的“事件驱动”的工作流引擎。 2.1.1. EOS工作流引擎的事件类型

以上的每个事件都是原子的不可分割的。其中一系列事件的集合通过EOS引擎事件调度机制实现我们平时在工作中经常遇到的如启动流程,结束工作项等等。(在事件类型类中EOS定义了29种事件,但在事件工厂类中EOS定义了26种类型。) 1.1.1. EOS工作流事件调度机制 EOS事件的调度服务是在工作流引擎初始化时通过服务工厂类加载到内存中(ServiceFactory.initEventService())。用户可以通过服务工厂类(ServiceFactory)取得JVM的唯一事件服务实例进行事务调度。所有的事件程序入口都是事件类(EventService),这个类其实是个接口,其有两个实现类,一个是单线程的实现类SingleThreadEventService (在实现代码中其实不是单线程,而是单例的对象),一个是多线程的实现类MulThreadThreadSvc,(其实现方式不在这里详细说明,多线程的类后面又跟了一大堆的线程池实现代码),在事件服务类中有一个属性类是WFEventDisposer,这个类包含了事

使用 GPars 解决常见并发问题

重庆IT论坛https://www.wendangku.net/doc/124433186.html, 在并发性时代,带有4、6 和16 个处理器核心的芯片变得很普遍,而且在不久的将来,我们会看到带有上百甚至上千个核心的芯片。这种处理能力蕴含着巨大的可能性,但对于软件开发人员来说,它也带来了挑战。最大限度地利用这些闪耀新核的需求推动了对并发性、状态管理和为两者构建的编程语言的关注热潮。 Groovy、Scala 和Clojure 等JVM 语言满足了这些需求。这三种都是较新的语言,运行于高度优化的JVM 之上,可以使用Java 1.5 中新增的强大的Java 并发库。尽管每种语言基于其原理采用不同的方法,不过它们都积极支持并发编程。 在本文中,我们将使用GPars,一种基于Groovy 的并发库,来检查模型以便解决并发性问题,比如后台处理、并行处理、状态管理和线程协调。 为何选择Groovy ?为何选择GPars ? Groovy 是运行于JVM 之上的一种动态语言。基于Java 语言,Groovy 移除了Java 代码中的大量正式语法,并添加了来自其他编程语言的有用特性。Groovy 的强大特性之一是它允许编程人员轻松创建基于Groovy 的DSL。(一个DSL 或域特定语言是一种旨在解决特定编程问题的脚本语言。参阅参考资料了解有关DSL 的更多信息。) 获取代码和工具 参阅参考资料部分下载Groovy、GPars 和本文中用到的其他工具。您可以随时下载本文的可执行代码样例。 GPars 或Groovy Parallel Systems 是一种Groovy 并发库,捕捉并发性和协调模型作为DSL。GPars 的构思源自其他语言的一些最受欢迎的并发性和协调模型,包括:?来自Java 语言的executors 和fork/join ?来自Erlang 和Scala 的actors ?来自Clojure 的agents ?来自Oz 的数据流变量 Groovy 和GPars 的结合成为展示各种并发性方法的理想之选。甚至不熟悉Groovy 的Java 开发人员也能轻松关注相关讨论,因为Groovy 的语法以Java 语言为基础。本文中的示例基于Groovy 1.7 和GPars 0.10。 回页首 后台和并行处理 一个常见的性能难题是需要等待I/O。I/O 可能涉及到从一个磁盘、一个web 服务或甚至是一名用户读取数据。当一个线程在等待I/O 的过程中被阻止时,将等待中的线程与原始执行线程分离开来将会很有用,这将使它能继续工作。由于这种等待是在后台发生的,所以我们称这种技术为后台处理。 例如,假设我们需要这样一个程序,即调用Twitter API 来找到针对若干JVM 语言的最新tweets 并将它们打印出来。Groovy 能够使用Java 库twitter4j 很容易就编写出这样的程序,如清单1 所示: 清单1. 串行读取tweets (langTweets.groovy) import twitter4j.Twitter import twitter4j.Query

线程池原理 C++实现

线程池原理及创建(C++实现) 时间:2010‐02‐25 14:40:43来源:网络 作者:未知 点击:2963次 本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关。另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整线程池中线程的数量。文章的最后,我们给出一个 本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关。另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整线程池中线程的数量。文章的最后,我们给出一个简单示例程序,通过该示例程序,我们会发现,通过该线程池框架执行多线程任务是多么的简单。 为什么需要线程池 目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短。 传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就是是“即时创建,即时销毁”的策略。尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。 我们将传统方案中的线程执行过程分为三个过程:T1、T2、T3。 T1:线程创建时间 T2:线程执行时间,包括线程的同步等时间 T3:线程销毁时间

那么我们可以看出,线程本身的开销所占的比例为(T1+T3) / (T1+T2+T3)。如果线程执行的时间很短的话,这比开销可能占到20%‐50%左右。如果任务执行时间很频繁的话,这笔开销将是不可忽略的。 除此之外,线程池能够减少创建的线程个数。通常线程池所允许的并发线程是有上界的,如果同时需要并发的线程数超过上界,那么一部分线程将会等待。而传统方案中,如果同时请求数目为2000,那么最坏情况下,系统可能需要产生2000个线程。尽管这不是一个很大的数目,但是也有部分机器可能达不到这种要求。 因此线程池的出现正是着眼于减少线程池本身带来的开销。线程池采用预创建的技术,在应用程序启动之后,将立即创建一定数量的线程(N1),放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个空闲线程,把任务传入此线程中运行。当N1个线程都在处理任务后,缓冲池自动创建一定数量的新线程,用于处理更多的任务。在任务执行完毕后线程也不退出,而是继续保持在池中等待下一次的任务。当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。 基于这种预创建技术,线程池将线程创建和销毁本身所带来的开销分摊到了各个具体的任务上,执行次数越多,每个任务所分担到的线程本身开销则越小,不过我们另外可能需要考虑进去线程之间同步所带来的开销。 构建线程池框架 一般线程池都必须具备下面几个组成部分: 线程池管理器:用于创建并管理线程池 工作线程: 线程池中实际执行的线程

多线程编程的详细说明完整版

VB .NET多线程编程的详细说明 作者:陶刚整理:https://www.wendangku.net/doc/124433186.html, 更新时间:2011-4-1 介绍 传统的Visual Basic开发人员已经建立了同步应用程序,在这些程序中事务按顺序执行。尽管由于多个事务多多少少地同时运行使多线程应用程序效率更高,但是使用先前版本的Visual Basic很难建立这类程序。 多线程程序是可行的,因为操作系统是多任务的,它有模拟同一时刻运行多个应用程序的能力。尽管多数个人计算机只有一个处理器,但是现在的操作系统还是通过在多个执行代码片断之间划分处理器时间提供了多任务。线程可能是整个应用程序,但通常是应用程序可以单独运行的一个部分。操作系统根据线程的优先级和离最近运行的时间长短给每一个线程分配处理时间。多线程对于时间密集型事务(例如文件输入输出)应用程序的性能有很大的提高。 但是也有必须细心的地方。尽管多线程能提高性能,但是每个线程还是需要用附加的内存来建立和处理器时间来运行,建立太多的线程可能降低应用程序的性能。当设计多线程应用程序时,应该比较性能与开销。 多任务成为操作系统的一部分已经很久了。但是直到最近Visual Basic程序员才能使用无文档记录特性(undocumented)或者间接使用COM组件或者操作系统的异步部分执行多线程事务。.NET框架组件为开发多线程应用程序,在System.Threading名字空间中提供了全面的支持。 本文讨论多线程的好处以及怎样使用Visual Basic .NET开发多线程应用程序。尽管Visual Basic .NET和.NET框架组件使开发多线程应用程序更容易,但是本文作了调整使其适合高级读者和希望从早期Visual Basic转移到Visual Basic .NET的开发人员。 多线程处理的优点 尽管同步应用程序易于开发,但是它们的性能通常比多线程应用程序低,因为一个新的事务必须等待前面的事务完成后才能开始。如果完成某个同步事务的时间比预想的要长,应用程序可能没有响应。多线程处理可以同时运行多个过程。例如,字处理程序能够在继续操作文档的同时执行拼写检查事务。因为多线程应用程序把程序分解为独立的事务,它们能通过下面的途径充分提高性能: l 多线程技术可以使程序更容易响应,因为在其它工作继续时用户界面可以保持激活。 l 当前不忙的事务可以把处理器时间让给其它事务。 l 花费大量处理时间的事务可以周期性的把时间让给其它的事务。 l 事务可以在任何时候停止。 l 可以通过把单独事务的优先级调高或调低来优化性能。 明确地建立多线程应用程序的决定依赖于几个因素。多线程最适合下面的情况:

11线程池的使用

第11章线程池的使用 第8章讲述了如何使用让线程保持用户方式的机制来实现线程同步的方法。用户方式的同步机制的出色之处在于它的同步速度很快。如果关心线程的运行速度,那么应该了解一下用户方式的同步机制是否适用。 到目前为止,已经知道创建多线程应用程序是非常困难的。需要会面临两个大问题。一个是要对线程的创建和撤消进行管理,另一个是要对线程对资源的访问实施同步。为了对资源访问实施同步,Wi n d o w s提供了许多基本要素来帮助进行操作,如事件、信标、互斥对象和关键代码段等。这些基本要素的使用都非常方便。为了使操作变得更加方便,唯一的方法是让系统能够自动保护共享资源。不幸的是,在Wi n d o w s提供一种让人满意的保护方法之前,我们已经有了一种这样的方法。 在如何对线程的创建和撤消进行管理的问题上,人人都有自己的好主意。近年来,我自己创建了若干不同的线程池实现代码,每个实现代码都进行了很好的调整,以便适应特定环境的需要。M i c r o s o f t公司的Windows 2000提供了一些新的线程池函数,使得线程的创建、撤消和基本管理变得更加容易。这个新的通用线程池并不完全适合每一种环境,但是它常常可以适合你的需要,并且能够节省大量的程序开发时间。 新的线程池函数使你能够执行下列操作: ? 异步调用函数。 ? 按照规定的时间间隔调用函数。 ? 当单个内核对象变为已通知状态时调用函数。 ? 当异步I / O请求完成时调用函数。 为了完成这些操作,线程池由4个独立的部分组成。表11 - 1显示了这些组件并描述了控制其行为特性的规则。 表11-1 线程池的组件及其行为特性

跟我学VS#语言编程技术——C#中的线程池Thread Pool技术及应用示例

1.1跟我学VS#语言编程技术——C#中的线程池Thread Pool技术及应用示例1.1.1C#中的线程池Thread Pool技术及应用示例 1、线程池Thread Pool技术概述 我们知道,大部分应用程序都是通过线程工作的,该线程等待一些引入的连接,一旦接收到一个连接,就会创建一个新的线程,并且要求这个线程服务于这个新的连接。 在此种工作方式下,当线程比较多的时候,系统的开销将会相当大。减少系统开销的一个方法就是使用一个线程池来处理,特别是关于在服务应用程序中使用线程的问题。 在服务器端的应用程序中几乎都采用了“线程池”技术,这主要是为了提高系统效率。因为如果服务器对应每一个请求就创建一个线程的话,在很短的一段时间内就会产生很多创建和销毁线程动作,导致服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。线程池就是为了尽量减少这种情况的发生。 2、线程池的工作原理 所谓线程池,就是在内存中等待工作的线程,系统会根据工作的情况,自动地创建并管理新的线程。其工作原理如下图所示:

3、ThreadPool类 在.NET中,通过System.Threading.ThreadPool类,每个进程都自动带有一个线程池,而且每个进程至多只能有一个线程池,可以在多个不同的行为间共享线程池。 上面我们已经知道了线程池主要用于服务引入的连接,但同时也还可以周期性地进行其它方面的服务,比如检查服务器上的磁盘空间,确保服务器高速运行,检查电子邮件以及服务客户连接等等。 其实,线程池可以使线程变得更容易,很明显还需要处理同步问题,但我们不需要真实地创建线程,.NET会帮助我们做这些。

Java定时任务ScheduledThreadPoolExecutor

Timer计时器有管理任务延迟执行("如1000ms后执行任务")以及周期性执行("如每500ms执行一次该任务")。但是,Timer存在一些缺陷,因此你应该考虑使用ScheduledThreadPoolExecutor作为代替品,Timer对调度的支持是基于绝对时间,而不是相对时间的,由此任务对系统时钟的改变是敏感的;ScheduledThreadExecutor只支持相对时间。 Timer的另一个问题在于,如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。Timer线程并不捕获异常,所以TimerTask抛出的未检查的异常会终止timer 线程。这种情况下,Timer也不会再重新恢复线程的执行了;它错误的认为整个Timer都被取消了。此时,已经被安排但尚未执行的TimerTask永远不会再执行了,新的任务也不能被调度了。 例子: packagecom.concurrent.basic; importjava.util.Timer; import java.util.TimerTask; public class TimerTest { private Timer timer = new Timer(); // 启动计时器 public void lanuchTimer() { timer.schedule(new TimerTask() { public void run() { throw new RuntimeException(); } }, 1000 * 3, 500); } // 向计时器添加一个任务 public void addOneTask() { timer.schedule(new TimerTask() { public void run() { System.out.println("hello world"); } }, 1000 * 1, 1000 * 5); }

tcl多线程编程的使用说明

321C H A P T E R II. Advanced Tcl 21Multi-Threaded Tcl Scripts This chapter describes the Thread extension for creating multi-threaded Tcl scripts. This Chapter is from Practical Programming in Tcl and Tk, 4th Ed. Copyright 2003 ? Brent Welch, Ken Jones https://www.wendangku.net/doc/124433186.html,/book/ T hread support, a key feature of many languages, is a recent addition to Tcl. That’s because the Tcl event loop supports features implemented by threads in most other languages, such as graphical user interface management, multi-client servers, asynchronous communication,and scheduling and timing operations. However, although Tcl’s event loop can replace the need for threads in many circumstances, there are still some instances where threads can be a better solution: ?Long-running calculations or other processing, which can “starve” the event loop ?Interaction with external libraries or processes that don’t support asynchro- nous communication ?Parallel processing that doesn’t adapt well to an event-driven model ?Embedding Tcl into an existing multi-threaded application What are Threads? Traditionally , processes have been limited in that they can do only one thing at a time. If your application needed to perform multiple tasks in parallel, you designed the application to create multiple processes. However, this approach has its drawbacks. One is that processes are relatively “heavy” in terms of the resources they consume and the time it takes to create them. For applications that frequently create new processes — for example, servers that create a new

100行Java代码构建一个线程池

100行Java代码构建一个线程池 本示例程序由三个类构成,第一个是TestThreadPool类,它是一个测试程序,用来模拟客户端的请求,当你运行它时,系统首先会显示线程池的初始化信息,然后提示你从键盘上输入字符串,并按下回车键,这时你会发现屏幕上显示信息,告诉你某个线程正在处理你的请求,如果你快速地输入一行行字符串,那么你会发现线程池中不断有线程被唤醒,来处理你的请求,在本例中,我创建了一个拥有10个线程的线程池,如果线程池中没有可用线程了,系统会提示你相应的警告信息,但如果你稍等片刻,那你会发现屏幕上会陆陆续续提示有线程进入了睡眠状态,这时你又可以发送新的请求了。 第二个类是ThreadPoolManager类,顾名思义,它是一个用于管理线程池的类,它的主要职责是初始化线程池,并为客户端的请求分配不同的线程来进行处理,如果线程池满了,它会对你发出警告信息。 最后一个类是SimpleThread类,它是Thread类的一个子类,它才真正对客户端的请求进行处理,SimpleThread在示例程序初始化时都处于睡眠状态,但如果它接受到了ThreadPoolManager类发过来的调度信息,则会将自己唤醒,并对请求进行处理。 首先我们来看一下TestThreadPool类的源码: //TestThreadPool.java 1 import java.io.*; 2 3 4 public class TestThreadPool 5 { 6 public static void main(String[] args) 7 { 8 try{ 9 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 10 String s; 11 ThreadPoolManager manager = new ThreadPoolManager(10); 12 while((s = br.readLine()) != null) 13 {

相关文档
相关文档 最新文档