关于Java您不知道的5件事(7)

2019-03-29 08:10

3. Executor

清单 1 和 清单 2 中的示例都存在一个重要的缺陷,它们要求您直接创建 Thread 对象。这可以解决一些问题,因为在一些 JVM 中,创建 Thread 是一项重量型的操作,重用现有 Thread 比创建新线程要容易得多。而在另一些 JVM 中,情况正好相反:Thread 是轻量型的,可以在需要时很容易地新建一个线程。当然,如果 Murphy 拥有自己的解决办法(他通常都会拥有),那么您无论使用哪种方法对于您最终将部署的平台都是不对的。

JSR-166 专家组(参见 参考资料)在一定程度上预测到了这一情形。Java 开发人员无需直接创建 Thread,他们引入了 Executor 接口,这是对创建新线程的一种抽象。如清单 3 所示,Executor 使您不必亲自对 Thread 对象执行 new 就能够创建新线程:

清单 3. Executor

Executor exec = getAnExecutorFromSomeplace(); exec.execute(new Runnable() { ... });

使用 Executor 的主要缺陷与我们在所有工厂中遇到的一样:工厂必须来自某个位置。不幸的是,与 CLR 不同,JVM 没有附带一个标准的 VM 级线程池。

Executor 类实际上 充当着一个提供 Executor 实现实例的共同位置,但它只有 new 方法(例如用于创建新线程池);它没有预先创建实例。所以您可以自行决定是否希望在代码中创建和使用 Executor 实例。(或者在某些情况下,您将能够使用所选的容器/平台提供的实例。)

ExecutorService 随时可以使用

尽管不必担心 Thread 来自何处,但 Executor 接口缺乏 Java 开发人员可能期望的某种功能,比如结束一个用于生成结果的线程并以非阻塞方式等待结果可用。(这是桌面应用程序的一个常见需求,用户将执行需要访问数据库的 UI 操作,然后如果该操作花费了很长时间,可能希望在它完成之前取消它。)

对于此问题,JSR-166 专家创建了一个更加有用的抽象(ExecutorService 接口),它将线程启动工厂建模为一个可集中控制的服务。例如,无需每执行一项任务就调用一次 execute(),ExecutorService 可以接受一组任务并返回一个表示每项任务的未来结果的未来列表。

4. ScheduledExecutorServices

尽管 ExecutorService 接口非常有用,但某些任务仍需要以计划方式执行,比如以确定的时间间隔或在特定时间执行给定的任务。这就是 ScheduledExecutorService 的应用范围,它扩展了 ExecutorService。

如果您的目标是创建一个每隔 5 秒跳一次的 “心跳” 命令,使用 ScheduledExecutorService 可以轻松实现,如清单 4 所示:

清单 4. ScheduledExecutorService 模拟心跳 import java.util.concurrent.*;

public class Ping{

public static void main(String[] args) {

ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);

Runnable pinger = new Runnable() { public void run() {

System.out.println(\ } };

ses.scheduleAtFixedRate(pinger, 5, 5, TimeUnit.SECONDS); } }

这项功能怎么样?不用过于担心线程,不用过于担心用户希望取消心跳时会发生什么,也不用明确地将线程标记为前台或后台;只需将所有的计划细节留给 ScheduledExecutorService。

顺便说一下,如果用户希望取消心跳,scheduleAtFixedRate 调用将返回一个 ScheduledFuture 实例,它不仅封装了结果(如果有),还拥有一个 cancel 方法来关闭计划的操作。

5. Timeout 方法

为阻塞操作设置一个具体的超时值(以避免死锁)的能力是 java.util.concurrent 库相比起早期并发特性的一大进步,比如监控锁定。

这些方法几乎总是包含一个 int/TimeUnit 对,指示这些方法应该等待多长时间才释放控制权并将其返回给程序。它需要开发人员执行更多工作 — 如果没有获取锁,您将如何重新获取? — 但结果几乎总是正确的:更少的死锁和更加适合生产的代码。(关于编写生产就绪代码的更多信息,请参见 参考资料 中 Michael Nygard 编写的 Release It!。)

结束语

java.util.concurrent 包还包含了其他许多好用的实用程序,它们很好地扩展到了 Collections 之外,尤其是在 .locks 和 .atomic 包中。深入研究,您还将发现一些有用的控制结构,比如 CyclicBarrier 等。

与 Java 平台的许多其他方面一样,您无需费劲地查找可能非常有用的基础架构代码。在编写多线程代码时,请记住本文讨论的实用程序和 上一篇文章 中讨论的实用程序。

参考资料

学习 ? ? ? ?

“关于 java.util.concurrent 您不知道的 5 件事,第 1 部分”(Ted Neward,developerworks,2010 “Release it!”(Michael Nygard,2007 年 3 月,Pragmatic Programmers):设计和搭建您的应用程JSR 166:了解关于 Executor 接口和通过此 JSR 引入的其他更改的更多信息。

“Java theory and practice: Concurrent collections classes”(Brian Goetz,developerWorks,2003 年 7 年 5 月):了解 5 个并发性类,它们为 List 和 Map 等传统集合注入了新的活力。 序,实现最长的运行时间、最高的性能和投资回报。

月):学习 Doug Lea 的 util.concurrent 包如何为标准集合类型 List 和 Map 注入新的活力。 ? ? ? 讨论 ?

加入 My developerWorks 中文社区。

“使用泛型和并发改善集合”(John Zukowski,developerWorks,2008 年 4 月):介绍 Java 6 中 Package java.util.concurrent, Java platform SE 6:进一步学习本文讨论的工具类。 developerWorks Java 技术专区:关于 Java 编程各个方面的数百篇文章。 Java Collections Framework 的变化。

关于 JAR 您不知道的 5 件事

Java Archive 不仅仅是一堆类

简介: 除了一些基础的 JAR 之外,许多 Java? 开发人员绝没有想到 — 仅使用它们就可以绑定类。但 JAR 不仅仅是一个重命名的 ZIP 文件。在本文中,您将学习如何最大限度地使用 Java Archive 文件,包括 jarring Spring 依赖项和配置文件的一些技巧。

发布日期: 2010 年 7 月 22 日 级别: 初级 其他语言版本: 英文

对于大多数 Java 开发人员来说,JAR 文件及其 “近亲” WAR 和 EAR 都只不过是漫长的 Ant 或 Maven 流程的最终结果。标准步骤是将一个 JAR 复制到服务器(或者,少数情况下是用户机)中的合适位置,然后忘记它。

事实上,JAR 能做的不止是存储源代码,您应该了解 JAR 还能做什么,以及如何进行。在这一期的 5 件事 系列中,将向您展示如何最大限度地利用 Java Archive 文件(有时候也可是 WAR 和 EAR),特别是在部署时。

由于有很多 Java 开发人员使用 Spring(因为 Spring 框架给传统的 JAR 使用带来一些特有的挑战),这里有几个具体技巧用于在 Spring 应用程序中处理 JAR 。

关于本系列

您觉得自己懂 Java 编程?事实是,大多数开发人员都只领会到了 Java 平台的皮毛,所学也只够应付工作。在本 系列 中,Ted Neward 深度挖掘 Java 平台的核心功能,揭示一些鲜为人知的事实,帮助您解决最棘手的编程困难。

我将以一个标准 Java Archive 文件产生过程的简单示例开始,这将作为以下技巧的基础。

把它放在 JAR 中

通常,在源代码被编译之后,您需要构建一个 JAR 文件,使用 jar 命令行实用工具,或者,更常用的是 Ant jar 任务将 Java 代码(已经被包分离)收集到一个单独的集合中,过程简洁易懂,我不想在这做过多的说明,稍后将继续说明如何构建 JAR。现在,我只需要存档 Hello,这是一个独立控制台实用工具,对于执行打印消息到控制台这个任务十分有用。如清单 1 所示:

清单 1. 存档控制台实用工具 package com.tedneward.jars;

public class Hello{

public static void main(String[] args) { System.out.println(\ } }

Hello 实用工具内容并不多,但是对于研究 JAR 文件却是一个很有用的 “脚手架”,我们先从执行此代码开始。

1. JAR 是可执行的

.NET 和 C++ 这类语言一直是 OS 友好的,只需要在命令行(helloWorld.exe)引用其名称,或在 GUI shell 中双击它的图标就可以启动应用程序。然而在 Java 编程中,启动器程序 —java— 将 JVM 引导入进程中,我们需要传递一个命令行参数(com.tedneward.Hello)指定想要启动的 main() 方法的类。

这些附加步骤使使用 Java 创建界面友好的应用程序更加困难。不仅终端用户需要在命令行输入所有参数(终端用户宁愿避开),而且极有可能使他或她操作失误以及返回一个难以理解的错误。

这个解决方案使 JAR 文件 “可执行” ,以致 Java 启动程序在执行 JAR 文件时,自动识别哪个类将要启动。我们所要做的是,将一个入口引入 JAR 文件清单文件(MANIFEST.MF 在 JAR 的 META-INF 子目录下),像这样:

清单 2. 展示入口点!

Main-Class: com.tedneward.jars.Hello

这个清单文件只是一个名值对。因为有时候清单文件很难处理回车和空格,然而在构建 JAR 时,使用 Ant 来生成清单文件是很容易的。在清单 3 中,使用 Ant jar 任务的 manifest 元素来指定清单文件:

清单 3. 构建我的入口点!

现在用户在执行 JAR 文件时需要做的就是通过 java -jar outapp.jar 在命令行上指定其文件名。就 GUI shell 来说,双击 JAR 文件即可。

2. JAR 可以包括依赖关系信息

似乎 Hello 实用工具已经展开,改变实现的需求已经出现。Spring 或 Guice 这类依赖项注入(DI)容器可以为我们处理许多细节,但是仍然有点小问题:修改代码使其含有 DI

容器的用法可能导致清单 4 所示的结果,如:

清单 4. Hello、Spring world! package com.tedneward.jars;

import org.springframework.context.*; import org.springframework.context.support.*;

public class Hello{

public static void main(String[] args) {

ApplicationContext appContext = new FileSystemXmlApplicationContext(\ ISpeak speaker = (ISpeak) appContext.getBean(\ System.out.println(speaker.sayHello()); } }

关于 Spring 的更多信息

这个技巧将帮助您熟悉依赖项注入和 Spring 框架。如果您需要温习其他主题,见 参考资料。

由于启动程序的 -jar 选项将覆盖 -classpath 命令行选项中的所有内容,因此运行这些代码时,Spring 必须是在 CLASSPATH和 环境变量中。幸运的是,JAR 允许在清单文件中出现其他的 JAR 依赖项声明,这使得无需声明就可以隐式创建 CLASSPATH,如清单 5 所示:

清单 5. Hello、Spring CLASSPATH!

value=\ ./lib/org.springframework.core-3.0.1.RELEASE-A.jar ./lib/org.springframework.asm-3.0.1.RELEASE-A.jar ./lib/org.springframework.beans-3.0.1.RELEASE-A.jar ./lib/org.springframework.expression-3.0.1.RELEASE-A.jar ./lib/commons-logging-1.0.4.jar\

注意 Class-Path 属性包含一个与应用程序所依赖的 JAR 文件相关的引用。您可以将它写成一个绝对引用或者完全没有前缀。这种情况下,我们假设 JAR 文件同应用程序 JAR 在同一个目录下。

不幸的是,value 属性和 Ant Class-Path 属性必须出现在同一行,因为 JAR 清单文件不能处理多个 Class-Path 属性。因此,所有这些依赖项在清单文件中必须出现在一行。当


关于Java您不知道的5件事(7).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:《诗经》两首练习及答案详解

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: