1 线 程 池 技 术 在 考 试 系 统 中 的 应 用 葛 萌 1, 于 博 2, 欧 阳 宏 基 ( 咸 阳 师 范 学 院 信 息 工 程 学 院, 咸 阳 712000) ( 河 南 建 筑 职 业 技 术 学 院 信 息 工 程 系, 郑 州 450064) 1 摘 要 : 当 较 大 规 模 客 户 端 并 发 请 求 服 务 器 端 应 用 程 序 时, 传 统 的 为 每 个 请 求 创 建 线 程 的 解 决 方 法 会 导 致 服 务 器 端 性 能 的 严 重 下 降 甚 至 死 机. 通 过 分 析 JDK 的 Executor 框 架, 从 工 作 原 理 核 心 线 程 池 对 象 执 行 策 略 等 方 面 详 细 描 述 了 线 程 池 模 型, 应 用 到 一 个 三 层 C/S 架 构 的 在 线 考 试 系 统 中, 给 出 了 服 务 端 的 设 计 架 构 和 实 现 代 码. 通 过 仿 真 测 试 证 明 了 线 程 池 技 术 在 解 决 较 大 并 发 访 问 方 面 的 稳 定 性. 关 键 词 : 线 程 池 ; Executor 框 架 ; 在 线 考 试 系 统 Application of Thread Pool Technology in Examination System GE Meng 1, YU Bo 2, OUYANG Hong-Ji 1 1 (Information Engineering College, Xianyang Normal University, Xianyang 721000, China) 2 (Information Engineering Department, Henan Technical College Of Construction, Zhenzhou 450064, China) Abstract: When large-scale client requests for applications in server concurrently, traditional solutions create a thread for every request, which will lead to a serious decline in server performance and even crash. By analyzing the JDK s Executor framework, the paper described in detail thread pool model from the principle of work, core thread pool object, implementation strategy and other aspects, applied the thread pool to a online examination system of three-layer C/S architecture, and gave the server s design framework and implementation code. Through simulation tests, the stability of the thread pool technical in the process of solving large concurrent access is proved. Key words: thread pool; Executor framework; online examination system 考 试 系 统 是 高 等 院 校 数 字 化 校 园 建 设 的 一 个 重 要 内 容, 通 过 代 替 传 统 的 纸 质 考 试 以 减 少 校 园 开 支 提 高 成 绩 的 客 观 性 和 公 正 性, 并 且 减 轻 教 师 的 工 作 强 度 [1]. 目 前 已 经 有 一 些 成 功 应 用 的 考 试 系 统, 例 如 全 国 计 算 机 等 级 考 试 软 件 资 格 ( 水 平 ) 考 试 系 统 等. 这 类 系 统 是 基 于 C/S 架 构 的 网 络 通 信 系 统, 学 生 使 用 客 户 端 程 序 在 一 个 较 小 规 模 ( 全 国 计 算 机 等 级 考 试 要 求 每 个 考 场 的 最 大 考 生 数 为 99 人 ) 的 局 域 网 内 与 唯 一 的 服 务 器 端 程 序 通 信. 当 考 生 的 并 发 量 较 小 时, 服 务 器 可 采 用 为 每 个 请 求 创 建 一 个 线 程 的 方 式 来 与 考 生 端 通 信, 实 现 难 度 也 不 大. 但 是 当 考 生 端 有 较 大 规 模 并 发 量 时, 此 方 式 将 会 极 大 的 消 耗 服 务 器 资 源, 因 为 大 量 线 程 间 频 繁 的 切 换 会 打 乱 系 统 固 有 的 切 换 周 期, 而 且 每 个 线 程 本 身 都 会 占 用 一 定 的 内 存, 因 此 会 导 致 服 务 器 端 响 应 缓 慢 甚 至 死 机 的 情 况. 为 了 解 决 上 述 问 题, 从 硬 件 方 面 可 以 采 取 提 高 服 务 器 内 存 CPU 个 数 以 及 搭 建 服 务 器 集 群 的 方 式 来 完 成. 本 文 主 要 从 软 件 设 计 方 面 通 过 引 入 线 程 池 技 术 来 解 决. 分 析 了 JDK 的 线 程 池 模 型 介 绍 了 一 个 在 线 考 试 系 统 框 架 及 主 要 功 能, 并 将 线 程 池 引 入 到 服 务 器 端 的 程 序 设 计 中, 详 细 描 述 了 实 现 过 程. 通 过 与 传 统 线 程 - 模 型 在 高 并 发 量 的 测 试 对 比, 证 明 了 线 程 池 的 可 行 性. 1 线 程 池 技 术 1.1 工 作 原 理 线 程 池 的 核 心 思 想 是 避 免 创 建 大 量 线 程 和 对 已 有 1 基 金 项 目 : 咸 阳 师 范 学 院 专 项 科 研 计 划 (2012XSYK070) 收 稿 时 间 :2015-07-29; 收 到 修 改 稿 时 间 :2015-10-08 System Construction 系 统 建 设 107
计 算 机 系 统 应 用 http://www.c-s-a.org.cn 2016 年 第 25 卷 第 4 期 线 程 的 复 用 [2,3]. 通 常 情 况 下, 服 务 器 端 程 序 在 启 动 时 创 建 若 干 数 量 的 线 程 对 象 并 缓 存 起 来, 此 时 它 们 处 于 空 闲 状 态 ; 当 客 户 端 请 求 到 来 并 被 服 务 器 接 受 后 ( 否 则 将 客 户 端 请 求 放 入 任 务 队 列 ), 从 线 程 池 中 唤 醒 一 个 线 程 与 该 客 户 端 进 行 通 信, 结 束 通 信 后 该 线 程 对 象 会 被 线 程 池 回 收, 准 备 用 它 执 行 下 一 次 任 务. 线 程 池 还 要 对 任 务 队 列 的 大 小 空 闲 线 程 的 销 毁 新 线 程 的 创 建 以 及 对 客 户 端 请 求 的 拒 绝 等 进 行, 这 样 可 以 更 好 的 利 用 线 程 对 象 并 对 服 务 器 进 行 有 效 的 保 护. 与 传 统 多 线 程 方 式 相 比, 线 程 池 具 有 以 下 两 点 优 势 [4] : 1 减 少 了 创 建 和 销 毁 线 程 的 次 数, 每 个 工 作 线 程 都 可 以 被 重 用, 能 执 行 多 个 任 务. 2 可 根 据 系 统 的 承 载 能 力 方 便 地 调 整 线 程 池 中 线 程 的 数 目, 防 止 消 耗 过 量 系 统 资 源 而 导 致 崩 溃. 1.2 JDK 线 程 池 体 系 结 构 由 于 线 程 池 与 业 务 是 无 关 的, 设 计 一 个 线 程 池 至 少 要 考 虑 以 下 几 个 方 面 [5,6] : 1 对 任 务 进 行 描 述 的 类, 包 含 线 程 池 中 线 程 执 行 的 所 有 信 息. 2 可 动 态 变 化 的 保 存 任 务 的 队 列. 3 线 程 池 器, 用 来 创 建 销 毁 线 程 池, 提 供 对 任 务 的 调 用 与 转 发. 4 处 理 任 务 的 工 作 线 程 类. 5 查 询 线 程, 用 来 检 测 任 务 的 完 成 情 况. 6 服 务 器 无 法 接 受 请 求 时 的 拒 绝 策 略. 同 时 还 要 考 虑 同 步 以 及 死 锁 风 险 等, 所 以 从 头 开 发 一 个 线 程 池 是 具 有 一 定 难 度 的. 为 了 简 化 使 用 线 程 池 的 难 度, JDK 从 1.5 版 本 开 始 在 java.util.concurrent 包 中 提 供 了 对 线 程 池 功 能 的 支 持, 相 关 核 心 接 口 类 的 层 次 关 系 如 图 1 所 示. 图 1 JDK 线 程 池 的 类 图 关 系 Executor 接 口 包 含 唯 一 的 execute 方 法, 通 过 Runnable 来 表 示 要 执 行 的 任 务, 将 任 务 的 提 交 与 执 行 操 作 进 行 解 耦. ExecutorService 接 口 是 真 正 意 义 上 的 线 程 池 顶 级 接 口, 其 中 定 义 了 提 交 FutureTask 任 务 的 方 法 以 及 若 干 与 线 程 池 生 命 周 期 相 关 的 方 法. 例 如 : shutdown() 方 法 用 来 关 闭 线 程 池, 以 前 提 交 的 任 务 会 被 执 行. shutdownnow() 方 法 强 制 关 闭 线 程 池, 取 消 所 有 运 行 中 的 任 务 以 及 在 工 作 队 列 中 等 待 的 任 务, 同 时 将 等 待 任 务 以 List 集 合 返 回. awaittermination 方 法 使 得 当 前 线 程 在 给 定 的 时 间 单 位 内 等 待 给 定 的 时 间 间 隔, 若 超 时 则 返 回 线 程 池 是 否 已 经 关 闭. isterminated() 方 法 判 断 线 程 池 是 否 已 经 处 于 关 闭 状 态. 抽 象 类 AbstractExecutorService 主 要 实 现 了 ExecutorService FutureTask 相 关 的 一 些 任 务 创 建 和 提 交 的 方 法. ThreadPoolExecutor 是 最 核 心 的 一 个 类, 用 来 表 示 线 程 池 对 象 ( 详 见 1.3 节 描 述 ). Scheduled ThreadPool Executor 具 有 ThreadPoolExecutor 的 大 部 分 功 能, 可 另 行 安 排 在 给 定 的 延 迟 后 或 者 定 期 执 行 任 务. 1.3 核 心 线 程 池 对 象 ThreadPoolExecutor 是 线 程 池 体 系 中 的 核 心 类, 用 来 创 建 和 维 护 线 程 池 对 象. 该 类 包 含 的 主 要 属 性 说 明 如 下 [7] : corepoolsize: 在 没 有 任 务 执 行 时, 线 程 池 中 所 保 留 的 线 程 数 目. maximumpoolsize: 线 程 池 中 同 时 工 作 的 线 程 数 的 最 大 值. keepalivetime: 当 线 程 数 大 于 核 心 数 时, 空 闲 线 程 等 待 新 任 务 的 最 长 时 间. 超 过 这 个 时 间 空 闲 线 程 没 有 接 到 任 务 就 会 被 回 收. unit: keepalivetime 参 数 的 时 间 单 位. workers: 正 在 被 线 程 执 行 的 任 务 集 合. workqueue: 等 待 被 执 行 的 任 务 队 列, 任 务 实 现 Runnable 接 口, 由 Execute 方 法 调 用 执 行. threadfactory: 线 程 工 厂, 用 于 在 线 程 池 需 要 新 创 建 线 程 的 时 候 创 建 线 程. defaulthandler: 当 前 池 中 线 程 数 已 达 到 最 大 值, 并 且 任 务 队 列 已 满, 线 程 池 中 无 法 承 载 线 程 时 的 处 理 策 略. 线 程 池 的 核 心 大 小 (corepoolsize) 最 大 值 (MaximumPoolSize) 和 存 活 大 小 (keepalivetime) 共 同 负 责 线 程 的 创 建 和 销 毁. 如 果 当 前 线 程 池 处 于 运 行 状 108 系 统 建 设 System Construction
态, 任 务 提 交 给 线 程 池 后 的 执 行 过 程 如 图 2 所 示. 通 过 调 节 线 程 池 的 基 本 大 小 和 存 活 时 间, 可 以 帮 助 线 程 池 回 收 空 闲 线 程 占 有 的 资 源, 从 而 使 这 些 资 源 可 以 用 于 执 行 其 它 工 作. 求, 可 以 通 过 创 建 ThreadPoolExecutor 对 象 来 定 制 策 略. 2 线 程 池 在 系 统 中 的 应 用 2.1 考 试 系 统 架 构 在 线 考 试 系 统 是 基 于 校 园 网 的 分 布 式 三 层 C/S 架 构 ( 如 图 3 所 示 ) [9], 分 别 为 : 表 示 层 业 务 层 和 数 据 层. 表 示 层 是 应 用 的 用 户 接 口 部 分, 采 用 Java Swing 来 设 计, 界 面 和 功 能 通 过 角 色 来 区 分. 业 务 层 是 系 统 的 核 心 内 容, 由 位 于 服 务 器 端 的 业 务 逻 辑 来 封 装, 通 过 RMI 向 表 示 层 提 供 服 务. 数 据 层 就 是 数 据 库 系 统, 为 业 务 层 和 表 示 层 提 供 数 据 服 务, 服 务 器 端 和 客 户 端 均 采 用 MySQL 数 据 库. 学 生 教 师 员 身 份 验 证 相 关 用 户 界 面 表 示 层 1.4 执 行 策 略 图 2 线 程 池 中 任 务 的 执 行 流 程 执 行 策 略 是 一 种 资 源 方 式, 通 过 限 制 并 发 的 数 量 来 确 保 应 用 程 序 不 会 由 于 资 源 耗 尽 而 崩 溃. 执 行 [8] 策 略 主 要 从 执 行 任 务 的 线 程 任 务 的 执 行 顺 序 任 务 并 发 执 行 的 个 数 任 务 队 列 中 等 待 执 行 的 个 数 对 哪 个 任 务 进 行 拒 绝 并 如 何 通 知 应 用 程 序 等 方 面 来 定 义 任 务 的 执 行, 从 而 确 保 应 用 程 序 具 有 较 好 的 性 能. Executors 类 通 过 工 厂 方 法 提 供 了 四 种 执 行 策 略 来 工 作 线 程, 分 别 是 : 1newSingleThreadPool: 确 保 所 有 任 务 按 照 提 交 的 先 后 顺 序 (FIFO LIFO 优 先 级 ) 由 唯 一 工 作 线 程 来 执 行, 该 策 略 不 算 真 正 意 义 上 的 并 发. 2newFixedThreadPool(Fix 类 型 ): 随 着 任 务 的 提 交 而 逐 个 创 建 线 程, 直 到 数 量 达 到 某 个 固 定 的 最 大 值. 3newCachedThreadPool(Cached 类 型 ): 线 程 池 大 小 完 全 依 赖 于 操 作 系 统 ( 或 者 JVM) 能 够 创 建 的 最 大 线 程 数, 并 且 能 根 据 当 前 任 务 的 数 量 来 调 整 线 程 池 中 线 程 的 个 数, 适 合 于 执 行 生 存 周 期 较 短 的 异 步 任 务. 4 newscheduledthreadpool: 创 建 一 个 大 小 无 限 的 线 程 池. 此 线 程 池 支 持 定 时 以 及 周 期 性 执 行 任 务 的 需 求. 以 上 策 略 中, 如 果 有 线 程 因 为 异 常 而 终 止 都 会 创 建 新 的 线 程 对 象 来 弥 补. 如 果 上 述 4 种 策 略 都 无 法 满 足 需 考 生 题 库 校 园 网 考 试 数 据 库 考 试 试 卷 评 阅 数 据 层 其 它 图 3 考 试 系 统 架 构 图 业 务 层 三 层 分 布 式 处 理 方 式 使 得 学 生 端 和 服 务 器 端 各 自 运 行 本 地 和 远 程 服 务, 可 以 解 决 数 据 层 与 业 务 层 的 分 离, 达 到 数 据 的 安 全 保 密. 由 于 考 生 端 程 序 运 行 于 本 地, 只 有 在 登 录 抽 取 试 题 与 提 交 试 卷 时 才 与 服 务 器 通 信, 答 题 阶 段 都 是 访 问 本 地 的 数 据 库, 这 样 可 以 提 高 数 据 读 取 效 率 减 轻 对 服 务 端 的 压 力. 2.2 基 于 线 程 池 的 服 务 器 端 设 计 由 于 考 生 只 有 在 登 录 抽 取 试 题 和 提 交 试 卷 这 三 个 操 作 涉 及 与 服 务 器 端 交 互, 考 虑 到 考 生 人 数 的 众 多 以 及 操 作 的 同 时 性, 所 以 服 务 器 端 采 用 线 程 池 技 术. 所 设 计 的 服 务 器 端 的 工 作 流 程 是 : 1) 初 始 化 系 统 信 息, 读 取 参 加 本 次 考 试 的 学 生 总 数, 该 数 值 用 于 创 建 线 程 System Construction 系 统 建 设 109
计 算 机 系 统 应 用 http://www.c-s-a.org.cn 2016 年 第 25 卷 第 4 期 池 的 任 务 队 列. 2) 创 建 线 程 池 对 象 以 及 线 程. 3) 创 建 基 于 TCP 协 议 的 ServerSocket 套 接 字, 在 某 一 端 口 监 听 考 生 端 的 请 求. 4) 当 有 考 生 端 请 求 到 来 时, 将 请 求 加 到 任 务 队 列, 从 线 程 池 中 拿 出 空 闲 线 程 去 接 受 请 求. 5) 线 程 用 于 监 测 线 程 池 中 线 程 的 数 量 及 时 进 行 新 线 程 的 创 建 与 销 毁. 6) 任 务 处 理 线 程 对 考 生 端 发 送 过 来 的 信 息 进 行 分 析, 根 据 登 录 抽 题 和 交 卷 操 作 分 别 进 行 处 理. 2.3 服 务 器 端 的 实 现 try{ final Socket conn=socket.accept(); exec.execute(new Runnable() { public void run() { handlerequest(conn); ); catch(ioexception e) { if(!exec.isshutdown()) logger.error(logutil.gettrace(e)); private void handlerequest(socket cs) { try { InputStream in=cs.getinputstream(); BufferedReader br=new BufferedReader(new InputStreamReader(in)); String command=br.readline(); if("getpaper".equalsignorecase(command)) {. 将 试 题 发 送 到 考 生 端 相 关 逻 辑.. handlerequest 为 任 务 对 象 的 核 心 方 法, 通 过 封 装 Socket 对 象, 根 据 读 取 输 入 流 中 第 一 行 字 符 串 信 息 判 断 当 前 客 户 端 的 请 求 类 型 并 执 行 相 应 的 逻 辑. 图 4 服 务 器 端 流 程 图 当 服 务 器 端 启 动 考 试 后, 创 建 基 于 TCP 协 议 的 ServerSocket 对 象. 如 果 服 务 器 成 功 接 受 考 生 端 的 请 求 便 返 回 一 个 Socket 对 象 ( 该 Socket 对 象 与 某 个 考 生 端 是 一 一 对 应 的 ). 通 过 实 现 Runnable 接 口 按 匿 名 内 部 类 方 式 创 建 任 务 对 象, 并 提 交 给 线 程 池 对 象 的 execute() 方 法 进 行 调 用. 相 关 核 心 代 码 如 下 所 示 : private final ExecutorService exec public void start() { ServerSocket socket=new ServerSocket(9999); While(!exec.isShutdown()) { 3 仿 真 测 试 服 务 器 可 用 内 存 4G, 操 作 系 统 为 Windows Server 2008, CPU 个 数 是 4, JDK 版 本 为 1.6. 服 务 器 端 分 别 运 行 多 线 程 Fixed 类 型 线 程 池 和 Cache 类 型 线 程 池 模 型 的 程 序 ; 客 户 机 通 过 测 试 程 序 来 模 拟 一 定 数 量 的 学 生 端 向 服 务 器 发 起 抽 取 试 题 请 求, 试 题 文 件 的 容 量 大 约 在 160KB 左 右, 统 计 三 种 情 况 下 学 生 端 读 取 试 题 的 平 均 时 间. 为 了 能 够 使 服 务 器 接 受 更 多 的 客 户 端 连 接 请 求, 采 用 ServerSocket(int port,int backlog) 构 造 方 法 来 创 建 服 务 器 端 套 接 字, 其 中 backlog 表 示 允 许 接 受 客 户 端 请 求 的 最 大 连 接 数, 测 试 过 程 过 不 断 改 变 此 值, 并 将 该 值 与 客 户 端 并 发 数 保 持 一 致. 对 于 Fixed 类 型 线 程 池, 根 据 文 献 [10] 的 研 究 最 大 线 程 数 =CPU 数 量 *2+2, 以 达 到 对 CPU 的 最 佳 利 用 率. 测 试 结 果 如 表 1 所 示. 110 系 统 建 设 System Construction
并 发 数 量 表 1 仿 真 测 试 线 程 池 方 式 多 线 程 方 式 Fix 类 型 Cache 类 型 科 目 的 考 试, 科 目 不 同 会 导 致 试 题 的 容 量 不 同, 进 而 研 究 文 件 容 量 网 络 传 输 速 度 硬 件 参 数 与 线 程 池 的 并 发 关 系, 进 一 步 优 化 系 统 的 性 能. 200 1341MS 1098MS 1331MS 300 1890MS 1665MS 1598MS 500 3673MS 2817MS 3083MS 800 5972MS 4872MS 5515MS 从 表 1 看 出, 随 着 并 发 数 量 的 增 加, 在 多 线 程 方 式 下 学 生 端 抽 到 试 题 的 平 均 时 间 逐 渐 增 大, 过 多 的 线 程 导 致 服 务 器 的 开 销 增 大 ; 线 程 池 方 式 下, 两 种 类 型 的 线 程 池 在 同 数 量 的 请 求 下, 学 生 端 抽 到 试 题 的 平 均 时 间 都 比 多 线 程 方 式 小, 这 是 因 为 线 程 池 实 现 了 线 程 的 重 用, 减 少 了 线 程 创 建 销 毁 的 时 间 消 耗. Fix 类 型 的 线 程 池 采 用 LinkedBlockingQueue 作 为 存 放 任 务 的 队 列, keepalivetime 值 为 0 秒 ; Cached 类 型 的 线 程 池 采 用 SynchronousQueue 作 为 存 放 任 务 的 队 列, keepalivetime 值 为 60 秒, 所 以 在 同 等 并 发 量 情 况 下, Fix 类 型 的 线 程 池 比 Cache 类 型 的 线 程 池 具 有 较 高 的 效 率. 测 试 结 果 表 明 采 用 线 程 池 技 术 的 本 系 统 是 可 行 的 高 效 的. 4 结 语 与 传 统 的 为 每 个 请 求 创 建 一 个 线 程 的 方 式 相 比, Executor 线 程 池 执 行 框 架 实 现 了 任 务 描 述 提 交 与 任 务 执 行 之 间 的 解 耦 ; 简 化 了 对 任 务 和 线 程 的, 提 高 了 系 统 效 率. 下 一 步 准 备 将 本 考 试 系 统 应 用 于 更 多 参 考 文 献 1 杜 静, 何 利 娟, 欧 阳 宏 基. 基 于 JDBC 与 DAO 模 式 的 在 线 考 试 系 统 设 计 与 实 现. 咸 阳 师 范 学 院 学 报,2014,29(2):26 29. 2 宋 开 卫, 石 坚, 程 哲. 基 于 线 程 池 的 数 据 采 集 器 在 网 管 中 的 应 用. 计 算 机 技 术 与 发 展,2007,17(7):210 212. 3 王 华, 马 亮, 顾 明. 线 程 池 技 术 研 究 与 应 用. 计 算 机 应 用 研 究,2005,22(11):141 142. 4 刘 新 强, 曾 兵 义. 用 线 程 池 解 决 服 务 器 并 发 请 求 的 方 案 设 计. 现 代 电 子 技 术,2011,34(15):141 143. 5 闫 保 中, 张 波. 线 程 池 技 术 在 车 流 监 控 系 统 中 的 应 用. 应 用 科 技, 2011,38(10):18 22. 6 王 文 武, 赵 卫 东, 王 志 成, 陈 悦, 韩 下 林. 高 性 能 服 务 器 底 层 网 络 通 信 模 块 的 设 计 方 法. 计 算 机 工 程,2009,35(3):103 105. 7 吴 玉, 刘 美 伶, 祝 建 中. 基 于 Java Executor 的 并 发 技 术 研 究. 计 算 机 时 代,2009,(2):8 10. 8 Goetz B, Peierls T, Bloch J. Java concurrency in practice. China Machine Press, 2014: 98 102. 9 田 屏. 基 于 C/S 架 构 的 考 试 系 统 设 计 [ 硕 士 学 位 论 文 ]. 贵 阳 : 贵 州 大 学,2008. 10 顾 永 新. 运 用 线 程 池 技 术 实 现 数 据 库 数 据 高 速 同 步. 信 息 系 统 工 程,2013,(3):97 99,126. System Construction 系 统 建 设 111