内容


IBM WebSphere 开发者技术期刊

使用 WebSphere Application Server 开发高性能 J2EE 线程

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: IBM WebSphere 开发者技术期刊

敬请期待该系列的后续内容。

此内容是该系列的一部分:IBM WebSphere 开发者技术期刊

敬请期待该系列的后续内容。

摘自 IBM WebSphere 开发者技术期刊

引言

IBM WebSphere Application Server 软件提供了以下两种机制,以支持 J2EE™ 应用程序开发人员在 Servlet 和 EJB 组件中安全地使用线程:

  • 异步 Bean
  • Commonj Timer and WorkManager for Application Servers 1.1 规范。

两种编程模式都允许您创建池线程和守护程序线程,以便运行 J2EE 业务逻辑。

在两种编程模式中,线程可以由不同的应用程序重用。当应用程序逻辑开始和终止时,这可以通过应用和删除线程内外的 J2EE 上下文信息来完成。因此,单个线程池可以由多个应用程序使用。每次使用线程时,其标识都会更改。

每次重用线程时,都必须更改线程的上下文,对于可能很少在这些线程执行活动的应用程序,开销会特别大。在这些情况下,需要一个组件范围的线程池,并在每个线程上具有固定的 J2EE 上下文。这可以通过使用 Asynchronous Beans EventSource 接口来完成。

本文介绍如何使用 Asynchronous Beans EventSource 构造线程工厂,文中还包括一个称为并发适配器 (Concurrent Adapter) 的可下载示例,它可以与第三方线程池实现一起使用,以创建能够在 WebSphere Application Server 上工作的快速线程池。

全局线程池

WebSphere Application Server 提供了高性能和高伸缩性的线程池实现。异步 Bean 和 Commonj 的 WorkManager 对所有池线程都使用此线程池。

由于 WorkManager 实例可用于全局命名空间,所以它们可以在多个应用程序之间共享,因此需要 J2EE 上下文切换。为完成此任务,WorkManager 在提交工作时会获取线程上 J2EE 上下文的快照。产生的对象将成为 WorkWithExecutionContext (WWEC) 对象(图 1)。

图 1. WorkWithExecutionContext
图 1. WorkWithExecutionContext

如果将WorkManager 用作全局线程池(图 2),则每次向线程分配工作时,提交给线程池的每项工作都将具有应用于线程(或从线程中删除)的应用程序上下文:

  1. 将工作提交给 WorkManager 线程池(蓝框)

    1. 获取 J2EE 应用程序上下文的快照,并将其作为 WWEC 对象与该工作存储在一起。
    2. 将 WWEC 添加到池的输入队列。
  2. 工作线程从输入队列提取一个 WWEC,并运行它。

    1. 获取工作线程上当前 J2EE 应用程序上下文的快照,以便在工作完成之后进行恢复。
    2. 将与 WWEC 一起存储的 J2EE 上下文应用到线程。
    3. 运行工作。
    4. 从线程删除 J2EE 上下文,并重新应用以前的上下文。
  3. 工作线程现在等待更多工作出现在输入队列上。

图 2. 与 WorkManager 共享的全局线程池
图 2. 与 WorkManager 共享的全局线程池
图 2. 与 WorkManager 共享的全局线程池

组件范围的线程池

如果线程池仅由单个应用程序或组件(如 Servlet 或 EJB)使用,并且要提交的工作可以容许单一通用的 J2EE 上下文标识,那么将自定义线程池与线程工厂一起使用可以显著提高性能。这是组件范围的线程池。

组件范围的线程池线程都将共享创建它的应用程序组件的 J2EE 上下文。例如,如果由启动 Bean 创建一个线程池,则每个线程将包含启动 Bean 的 start() 方法的上下文,并且每个线程的行为就好像在该启动 Bean 的范围中运行一样。因此,启动 Bean 是线程池实例的所有者。不论提交工作的 Servlet 或 EJB 如何,所有业务逻辑都将在 java:comp 命名空间和启动 Bean 的 start() 方法的安全上下文中运行。

组件范围的线程池中的所有线程都是异步 Bean 守护程序线程,并且具有与创建它的应用程序相同的生命周期。如果应用程序结束,将调用池中每个守护程序工作线程的 release() 方法。

在使用自定义组件范围的线程池(图 3)时,将使用 WorkManager 创建的守护程序线程初始化池中的每个工作线程。WorkManager 成为线程工厂。每个线程将共享池创建程序的同一 J2EE 上下文:

  1. 将可运行线程提交到自定义线程池。

  2. 线程池工作线程从输入队列提取下一个 WWEC,并运行它。每个工作线程都有适用于它的线程池创建程序组件的 J2EE 上下文。

    1. 可运行线程在 J2EE 工作线程上运行。
    2. 当完成时,J2EE 工作线程仍保持活动状态。
  3. J2EE 工作线程现在等待更多工作出现在输入队列上。

图 3. 组件范围的线程池
图 3.  组件范围的线程池
图 3. 组件范围的线程池

自定义线程池

Asynchronous Beans WorkManager 不能外部化线程池,所以没有任何方法更改缺省行为。要实现组件范围的线程池,必须将第三方线程池与异步 Bean 的 J2EE 上下文切换功能结合使用。

现有的几个线程池实现可以很好地与此模式一起使用。一种广泛接受的实现是 Doug Lea 的 EDU.oswego.cs.dl.util.concurrent.PooledExecutor,它运行在 J2SE 1.2 及更高版本上。此线程池已发展为 J2SE 5 的 java.util.concurrent.ThreadPoolExecutor,它还向下移植到 J2SE 1.4。

PooledExecutor 和 ThreadPoolExecutor 实现都位于公共域中,并且可以下载(请参阅参考资料)。

下面是 WebSphere Application Server 各版本建议的线程池实现:

  • WebSphere Application Server Enterprise V5.0, J2SE 1.3

    • EDU.oswego.cs.dl.util.concurrent.PooledExecutor
  • WebSphere Busness Integration Server Foundation V5.1 和
    WebSphere Application Server(所有版本)V6.0,J2SE 1.4:

    • edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor
    • EDU.oswego.cs.dl.util.concurrent.PooledExecutor
  • WebSphere Application Server(所有版本)V6.1,J2SE 5:

    • edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor
    • EDU.oswego.cs.dl.util.concurrent.PooledExecutor
    • java.util.concurrent.ThreadPoolExecutor

上述实现非常相似,并且都利用了线程工厂,它们支持插入识别 J2EE 的自定义线程工厂。在这种情况下,自定义线程工厂是 Asynchronous Beans EventSource 的包装程序。(请参阅 Concurrent Adapter 示例。)这些实现的行为类似于 WebSphere 线程池实现。如果使用的是 J2SE 1.4,则建议使用 java.util.concurrent 的向下移植,因为它具有更多的功能,并能够简化以后到 J2SE 5 的迁移。如果使用的是 J2SE 5,则建议直接使用 java.util.concurrent。

Asynchronous Beans EventSource

EventSource 是 Asynchronous Beans WorkManager 的一项机制,它支持 J2EE 上下文从一个线程到任何其他线程的动态应用。它提供了同一进程中的应用程序或安全上下文之间安全通信的方法。

例如,当股票价格改变时,如果要通知 Servlet,它可以在知名的 EventSource 上注册一个侦听器。当股票价格守护程序发布更改时,将通过系统帐户通知每个侦听器,但仅在侦听器的安全上下文中进行通知(图 4)。

图 4. 使用 EventSource 的安全上下文切换
图 4. 使用 EventSource 的安全上下文切换
图 4. 使用 EventSource 的安全上下文切换

同一技术可以应用到任何 POJO(传统 Java™ 对象),后者可以使用 EventSource 进行打包,以便针对单个方法调用切换对象的 J2EE 上下文。EventSources 使用 java.lang.reflect.Proxy 对象将每个对象实例与专用的 J2EE java.lang.reflect.InvocationHandler 打包在一起。使用代理来调用 Java 对象方法时,J2EE 处理程序将自动在注册侦听器时捕获的线程上应用 J2EE 上下文。

当在范围外的线程上执行方法时,此功能特别有用;例如,启动线程池中的线程。

如果没有 EventSource 代理,则新的线程将没有 J2EE 上下文。线程池将实例化新的 java.util.Thread 对象,并调用 start() 方法。

EventSource 使我们能够执行可以插入到 PooledExecutor 或 ThreadPoolExecutor 的 ThreadFactory 实现。创建 ThreadFactory 的 J2EE 组件的 J2EE 上下文将被保留,并在启动新的工作线程之前重新应用到线程。切换上下文的所有开销都一次性应用到线程。

由于 Commonj WorkManager 规范中不存在 EventSource 对象,所以不可能构建线程工厂。仅当使用异步 Bean 时,线程工厂才可使用。

实现线程工厂

在本文中,我们提供了示例线程工厂实现,这些实现支持使用 J2SE 5、J2SE 1.4 的组件范围的自定义线程池(使用 J2SE 5 java.util.concurrent.ThreadPoolExecutor 的向下移植或在 WebSphere 应用程序中使用 dl.util.concurrent.PooledExecutor)。(有关先决条件和如何构建 WASThreadFactory 的详细信息,请参阅下载文件中包括的 com.ibm.websphere.sample.concurrentadapter 包中的 Javadoc。)

WASThreadFactory

WASThreadFactory 是 ThreadFactory 的实现。它使用 WASThreadFactoryBase基类来支持使用 J2SE 5 和 ThreadFactory 的 dl.concurrent.util 版本的单个实现(图 5)。

图 5. WASThreadFactory 类关系图
图 5. WASThreadFactory 类关系图
图 5. WASThreadFactory 类关系图

WASThreadFactory 接受 WorkManager 作为构造函数的参数,并使用它创建 EventSource 并为 WorkManager 注册侦听器代理。当从 WASThreadFactory 请求线程时,将使用 WorkManager 创建线程,但是该线程将包含创建 WASThreadFactory 实例的组件的 J2EE 上下文。这也称为单上下文的线程池。

单上下文和多上下文的线程池

在使用自定义线程池(如 ThreadPoolExecutor)时,可能不需要在单个 J2EE 上下文中运行工作。例如,可能需要传播提交程序的安全上下文。

在这种情况下,可以使用识别 J2EE 上下文的执行程序 (ContextExecutor),以便在执行之前将当前 J2EE 上下文附加到可运行线程。池中的每个工作线程都使用 WASThreadFactory 创建程序的 J2EE 上下文进行填充,但是当使用 ContextExecutor 时,可运行线程首先将提交程序的 J2EE 上下文应用到线程,然后在完成时删除它。

使用同一 ThreadPoolExecutor 直接将工作提交到可以利用缺省 J2EE 上下文的池。使用 ContextExecutor 包装程序将工作提交到 ThreadPoolExecutor,但要带有提交程序的上下文。

图 6. 带有缺省的和多上下文的自定义线程池
图 6. 带有缺省的和多上下文的自定义线程池
图 6. 带有缺省的和多上下文的自定义线程池

用法示例:WASThreadFactory

要使用 WASThreadFactory,应用程序必须首先查找 Asynchronous Beans WorkManager,创建一个 WASThreadFactory 实例,然后构造新的线程池。下载文件示例中可提供使用 Servlet 的工作例子。

// Lookup the WorkManager and construct the WASThreadFactory
InitialContext ctx = new InitialContext();
WorkManager wm = (WorkManager)ctx.lookup("java:comp/env/wm/default");
ThreadFactory tf = new WASThreadFactory(wm);

// Create a ThreadPoolExecutor using a bounded buffer
BlockingQueue q = new ArrayBlockingQueue(10);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, 10, 5000, TimeUnit.MILLISECONDS, q, tf);

// Use the submit or execute methods to submit work to the pool
pool.submit(myRunnable);

用法示例:ContextExecutor

使用带 ContextExecutor 的 WASThreadFactory 将可运行线程提交给带提交程序的 J2EE 上下文的自定义线程池与创建 ContextExecutor 实例一样简单。也可以使用 ContextExecutorService 来跟踪提交的任务的状态。

// Create a ThreadPoolExecutor
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, 10, 5000, TimeUnit.MILLISECONDS, q, tf);

// Wrap the ThreadPoolExecutor with a ContextExecutor
ContextExecutor cePool = new ContextExecutor(wm, pool);

// Use the ContextExecutor.execute() method to submit work to the pool.
cePool.execute(myRunnable)

安装并运行示例

本文包括三个示例应用程序;分别对应一个描述的线程池实现。每个应用程序包含一个可以在 WebSphere Application Server 上安装的 EAR。每个 EAR 包含:

  • 一个 Web 模块 (WAR)
  • ABConcurrencyUtils.jar 实用工具 JAR 文件,它包含对所有示例(包括 WASThreadFactory)通用的代码
  • 实用工具 JAR,它包括用于线程池实现的特定代码。

每个 WAR 包含三个 Servlet:

  • FactoryTestServlet:一个简单的示例,显示如何创建 WASThreadFactory 并向其提交大量的可运行线程任务。

  • ABBenchmarkServlet:该示例显示在不使用 WASThreadFactory 时运行异步 Bean 工作对象所用的微秒数。

  • FactoryBenchmarkServlet:该示例显示在使用 WASThreadFactory 时运行可运行线程所用的微秒数。

每个模块包括源和二进制,并可以直接将其导入到 IBM Rational® Application Developer V6。(在撰写本文时,Rational Application Developer 当前不支持 JDK 5。JDK5 示例中的 JDK5 实用工具 JAR 将不能在 Rational Application Developer 中编译,必须单独构建。)

先决条件

每个示例都需要异步 Bean 和带有 JNDI 名称(缺省情况下为 wm)的 WorkManager;在安装过程中缺省创建该 WorkManager。这些示例仅在 WebSphere Application Server 的单服务器版上进行了测试。尽管这些示例没有在 WebSphere Application Server Network Deployment 或 Rational Application Developer 的单元测试环境或者 WebSphere Studio Application Developer Integration Edition 下进行测试,但我们预计这些示例可以在这些环境中按预期的方式运行。

示例 1:ABConcurrencyTester_JDK5.ear

可以在 WebSphere Application Server V6.1 及更高版本上安装并运行此示例。它利用 J2SE 5 中包括的 java.util.concurrent.ThreadPoolExecutor。

要运行此示例,请按照下列步骤操作:

  1. 启动 WebSphere Application Server。
  2. 使用 wsadmin 脚本或管理控制台安装 ABConcurrencyTester_JDK5.ear。使用缺省选项。
  3. 启动 ABConcurrencyTester_JDK5 应用程序。
  4. 使用以下 URL 运行示例(其中 <host> 是 IP 地址或应用服务器主机的名称,<port> 是应用服务器的 HTTP 侦听器端口):

    http://<host>:<port>/ABConcurrencyTester_JDK5

示例 2:ABConcurrencyTester_JDK14.ear

可以在 IBM WebSphere Business Integration Server Foundation V5.1 和 WebSphere Application Server(所有版本)V6.0 及更高版本上安装并运行此示例。它利用 JDK 1.4 的 java.util.concurrent 向下移植。

要运行此示例,请按照下列步骤操作:

  1. http://dcl.mathcs.emory.edu/util/backport-util-concurrent/ 下载 backport-util-concurrent.jar。
  2. 将 backport-util-concurrent.jar 添加到 ABConcurrencyTester_JDK14.ear 的根目录中。
  3. 使用 wsadmin 脚本或管理控制台安装 ABConcurrencyTester_JDK14.ear。使用缺省选项。
  4. 启动 ABConcurrencyTester_JDK14 应用程序。
  5. 使用以下 URL 运行示例(其中 <host> 是 IP 地址或应用服务器主机的名称,<port> 是应用服务器的 HTTP 侦听器端口):

    http://<host>:<port>/ABConcurrencyTester_JDK14

示例 3:ABConcurrencyTester_DL.ear

此示例可以安装在 WebSphere Application Server Enterprise V5.0.2、WebSphere Business Integration Server Foundation V5.1 和 WebSphere Application Server(所有版本)V6.0 及更高版本上。它利用运行在 JDK 1.2 及更高版本上的 dl.util.concurrent 包。

要运行此示例,请按照下列步骤操作:

  1. 使用 JDK 1.3 编译器(如果部署到 WebSphere Application Server Enterprise V5.0 或更高版本)或 JDK 1.4 编译器(如果部署到 WebSphere Business Integration Server Foundation V5.1 或更高版本)下载并构建 concurrent.jar。该实用工具附带可以创建 concurrent.jar 的 ANT 脚本。(您可以使用应用服务器 bin 目录中的 ws_ant.bat 或 ws_ant.sh 脚本来构建 concurrent.jar。)从 http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html 下载该实用工具。
  2. 将 concurrent.jar 重新命名为 dl-util-concurrent.jar。
  3. 将 dl-util-concurrent.jar 添加到 ABConcurrencyTester_DL.ear 的根目录。
  4. 使用 wsadmin 脚本或管理控制台安装 ABConcurrencyTester_DL.ear。使用缺省选项。
  5. 启动 ABConcurrencyTester_DL 应用程序。
  6. 使用以下 URL 运行示例(其中 <host> 是 IP 地址或应用服务器主机的名称,<port> 是应用服务器的 HTTP 侦听器端口):

    http://<host>:<port>/ABConcurrencyTester_DL

结束语

异步 Bean 提供可以由多个应用程序使用的有效而又安全的全局线程池。在需要专门的线程池时,可以使用 Asynchronous Beans EventSource 来创建 ThreadFactory。可以使用识别 J2EE 的 ThreadFactory 来创建使用固定 J2EE 上下文填充的线程。

在本文的 Concurrent Adapter 示例中使用的 WASThreadFactory 使 J2EE 应用程序开发人员能够自由地利用任何高级线程使用模式,而不降低性能。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere, Java technology
ArticleID=160151
ArticleTitle=IBM WebSphere 开发者技术期刊: 使用 WebSphere Application Server 开发高性能 J2EE 线程
publish-date=09172006