您的位置 首页 java

java中的线程池是如何实现的

1 : Java 中 的 线 程 池 是 如 何 实 现 的 ?

·在 Java 中 , 所 谓 的 线 程 池 中 的 “ 线 程 ” , 其 实 是 被 抽 象 为 了

一 个 静 态 内 部 类 Worker, 它 基 于 AQS 实 现 , 存 放 在 线 程 池 的

HashSet<Worker> workers 成 员 变 量 中 ; 而 需 要 执 行 的 任 务 则 存 放 在 成 员 变 量 workQueue ( BlockingQueue<Runnable> workQueue) 中 。

这 样 , 整 个 线 程 池 实 现 的 基 本 思 想 就 是 : 从 workQueue 中 不 断 取 出 需 要 执 行 的 任 务 , 放 在 Workers 中 进 行 处 理 。

2: 创 建 线 程 池 的 几 个 核 心 构 造 参 数 ?

Java 中 的 线 程 池 的 创 建 其 实 非 常 灵 活 , 我 们 可 以 通 过 配 置 不 同 的 参 数 , 创 建 出 行 为 不 同 的 线 程 池 , 这 几 个 参 数 包 括 :

corePoolSize: 线 程 池 的 核 心 线 程 数 。

maximumPoolSize: 线 程 池 允 许 的 最 大 线 程 数 。

keepAliveTime: 超 过 核 心 线 程 数 时 闲 置 线 程 的 存 活 时 间 。

workQueue: 任 务 执 行 前 保 存 任 务 的 队 列 , 保 存 由 execute 方 法 提 交

的 Runnable 任 务 。

3: 线 程 池 中 的 线 程 是 怎 么 创 建 的 ?

是 一 开 始 就 随 着 线 程 池 的 启 动 创 建 好 的 吗 ?显 然 不 是 的 。 线 程 池 默 认 初 始 化 后 不 启 动 Worker, 等 待 有 请 求 时 才 启 动 。 每 当 我 们 调 用 execute() 方 法 添 加 一 个 任 务 时 , 线 程 池 会 做 如 下 判 断 :

·如 果 正 在 运 行 的 线 程 数 量 小 于 corePoolSize, 那 么 马 上 创 建 线 程 运 行 这 个 任 务 ; 如 果 正 在 运 行 的 线 程 数 量 大 于 或 等 于 corePoolSize,

那 么 将 这 个 任 务 放 入 队 列 ;

如 果 这 时 候 队 列 满 了 , 而 且 正 在 运 行 的 线 程 数 量 小 于maximumPoolSize, 那 么 还 是 要 创 建 非 核 心 线 程 立 刻 运 行 这 个 任 务 ;

如 果 队 列 满 了 , 而 且 正 在 运 行 的 线 程 数 量 大 于 或 等 于maximumPoolSize, 那 么 线 程 池 会 抛 出 异 常 RejectExecution Exception 。当 一 个 线 程 完 成 任 务 时 , 它 会 从 队 列 中 取 下 一 个 任 务 来 执 行 。 当 一 个线 程 无 事 可 做 , 超 过 一 定 的 时 间 ( keepAliveTime) 时 , 线 程 池 会 判断 。

如 果 当 前 运 行 的 线 程 数 大 于 corePoolSize, 那 么 这 个 线 程 就 被 停 掉 。 所 以 线 程 池 的 所 有 任 务 完 成 后 , 它 最 终 会 收 缩 到 corePoolSize 的 大 小 。

4: 既 然 提 到 可 以 通 过 配 置 不 同 参 数 创 建 出 不 同 的 线 程 池 , 那 么 Java 中 默 认 实 现 好 的 线 程 池 又 有 哪 些 呢 ? 请 比 较 它 们 的 异 同 。

1. SingleThreadExecutor 线 程 池 这 个 线 程 池 只 有 一 个 核 心 线 程 在 工 作 , 也 就 是 相 当 于 单 线 程 串 行 执 行 所 有 任 务 。 如 果 这 个 唯 一 的 线 程 因 为 异 常 结 束 , 那 么 会 有 一 个 新 的 线 程 来 替 代 它 。 此 线 程 池 保 证 所 有 任 务 的 执 行 顺 序 按 照 任 务 的 提 交 顺 序 执 行 。

corePoolSize: 1, 只 有 一 个 核 心 线 程 在 工 作 。

maximumPoolSize: 1。

keepAliveTime: 0L。

workQueue: new LinkedBlockingQueue<Runnable>(), 其 缓 冲 队 列是 无 界 的 。

2. FixedThreadPool 线 程 池

FixedThreadPool 是 固 定 大 小 的 线 程 池 , 只 有 核 心 线 程 。 每 次 提 交

一 个 任 务 就 创 建 一 个 线 程 , 直 到 线 程 达 到 线 程 池 的 最 大 大 小 。 线 程 池 的 大 小 一 旦 达 到 最 大 值 就 会 保 持 不 变 , 如 果 某 个 线 程 因 为 执 行 异 常 而 结 束 , 那 么 线 程 池 会 补 充 一 个 新 线 程 。FixedThreadPool 多 数 针 对 一 些 很 稳 定 很 固 定 的 正 规 并 发 线 程 , 多 用 于 服 务 器 。

corePoolSize: nThreads

maximumPoolSize: nThreads

keepAliveTime: 0L

workQueue: new LinkedBlockingQueue<Runnable>(), 其 缓 冲 队 列是 无 界 的 。

2. CachedThreadPool 线 程 池 CachedThreadPool 是 无 界 线 程 池 , 如 果 线 程 池 的 大 小 超 过 了 处 理 任 务 所 需 要 的 线 程 , 那 么 就 会 回 收 部 分 空 闲 ( 60 秒 不 执 行 任 务 ) 线 程 , 当 任 务 数 增 加 时 , 此 线 程 池 又 可 以 智 能 的 添 加 新 线 程 来 处 理 任 务 。

线 程 池 大 小 完 全 依 赖 于 操 作 系 统 ( 或 者 说 JVM) 能 够 创 建 的 最 大 线 程 大 小 。 SynchronousQueue 是 一 个 是 缓 冲 区 为 1 的 阻 塞 队 列 。

缓 存 型 池 子 通 常 用 于 执 行 一 些 生 存 期 很 短 的 异 步 型 任 务 , 因 此 在 一 些 面 向 连 接 的 daemon 型 SERVER 中 用 得 不 多 。 但 对 于 生 存 期 短 的 异 步 任 务 , 它 是 Executor 的 首 选 。

corePoolSize: 0

maximumPoolSize: Integer.MAX_VALUE

keepAliveTime: 60L

workQueue: new SynchronousQueue<Runnable>(),一 个 是 缓 冲 区 为 1 的 阻 塞 队 列 。

3. ScheduledThreadPool 线 程 池ScheduledThreadPool: 核 心 线 程 池 固 定 , 大 小 无 限 的 线 程 池 。 此 线 程 池 支 持 定 时 以 及 周 期 性 执 行 任 务 的 需 求 。

创 建 一 个 周 期 性 执 行 任 务 的 线

程 池 。 如 果 闲 置 , 非 核 心 线 程 池 会 在 DEFAULT_KEEPALIVEMILLIS 时间 内 回 收 corePoolSize: corePoolSize

maximumPoolSize: Integer.MAX_VALUE

keepAliveTime: DEFAULT_KEEPALIVE_MILLIS

workQueue: new DelayedWorkQueue()

5 : 如 何 在 Java 线 程 池 中 提 交 线 程 ?

线 程 池 最 常 用 的 提 交 任 务 的 方 法 有 两 种 :

1. execute(): ExecutorService.execute 方 法 接 收 一 个 Runable 实

例 , 它 用 来 执 行 一 个 任 务 :

2. submit(): ExecutorService.submit() 方 法 返 回 的 是 Future 对 象 。 可 以 用 isDone() 来 查 询 Future 是 否 已 经 完 成 , 当 任 务 完 成 时 , 它 具 有 一个 结 果 , 可 以 调 用 get() 来 获 取 结 果 。 也 可 以 不 用 isDone()进 行 检 查 就 直 接 调 用 get(), 在 这 种 情 况 下 , get() 将 阻 塞 , 直 至 结 果 准 备 就 绪 。Java 内 存 模 型 相 关 问 题

6: 什 么 是 Java 的 内 存 模 型 , Java 中 各 个 线 程 是 怎 么 彼 此 看 到 对 方 的 变 量 的 ?

Java 的 内 存 模 型 定 义 了 程 序 中 各 个 变 量 的 访 问 规 则 , 即 在 虚 拟 机 中 将 变 量 存 储 到 内 存 和 从 内 存 中 取 出 这 样 的 底 层 细 节 。 此 处 的 变 量 包 括 实 例 字 段 、 静 态 字 段 和 构 成 数 组 对 象 的 元 素 , 但 是 不 包 括 局 部 变 量 和 方 法 参 数 , 因 为 这 些 是 线 程 私 有 的 , 不 会 被 共 享 , 所 以 不 存 在 竞 争 问 题 。

Java 中 各 个 线 程 是 怎 么 彼 此 看 到 对 方 的 变 量 的 呢 ?

Java 中 定 义 了 主 内 存 与 工 作 内 存 的 概 念 : 所 有 的 变 量 都 存 储 在 主 内 存 , 每 条 线 程 还 有 自 己 的 工 作 内 存 , 保 存 了 被 该 线 程 使 用 到 的 变 量 的 主 内 存 副 本 拷 贝 。

线 程 对 变 量 的 所 有 操 作 ( 读 取 、 赋 值 ) 都 必 须 在 工 作 内 存 中 进 行 , 不 能 直 接 读 写 主 内 存 的 变 量 。 不 同 的 线 程 之 间 也 无 法 直 接 访 问 对 方 工 作 内 存 的 变 量 , 线 程 间 变 量 值 的 传 递 需 要 通 过 主 内 存 。

7 : 请 谈 谈 volatile 有 什 么 特 点 , 为 什 么 它 能 保 证 变 量 对 所 有 线 程 的 可 见 性 ?

关 键 字 volatile 是 Java 虚 拟 机 提 供 的 最 轻 量 级 的 同 步 机 制 。 当

一 个 变 量 被 定 义 成 volatile 之 后 , 具 备 两 种 特 性 :

1. 保 证 此 变 量 对 所 有 线 程 的 可 见 性 。 当 一 条 线 程 修 改 了 这 个 变 量 的 值 , 新 值 对 于 其 他 线 程 是 可 以 立 即 得 知 的 。 而 普 通 变 量 做 不 到 这 一 点 。

2. 禁 止 指 令 重 排 序 优 化 。 普 通 变 量 仅 仅 能 保 证 在 该 方 法 执 行 过 程 中 , 得 到 正 确 结 果 , 但 是 不 保 证 程 序 代 码 的 执 行 顺 序 。

Java 的 内 存 模 型 定 义 了 8 种 内 存 间 操 作 : lock 和 unlock 把 一 个 变 量 标 识 为 一 条 线 程 独 占 的 状 态 。

把 一 个 处 于 锁 定 状 态 的 变 量 释 放 出 来 , 释 放 之 后 的 变 量 才 能 被 其 他 线 程 锁 定 。 read 和 write 把 一 个 变 量 值 从 主 内 存 传 输 到 线 程 的 工 作 内 存 , 以 便 load。 把 store 操 作 从 工 作 内 存 得 到 的 变 量 的 值 , 放 入 主 内 存 的 变 量 中 。

load 和 store 把 read 操 作 从 主 内 存 得 到 的 变 量 值 放 入 工 作 内 存 的 变 量 副 本 中 。

·把 工 作 内 存 的 变 量 值 传 送 到 主 内 存 , 以 便 write。 use 和 assgin。

把 工 作 内 存 变 量 值 传 递 给 执 行 引 擎 。 将 执 行 引 擎 值 传 递 给 工 作 内 存 变 量 值 。 volatile 的 实 现 基 于 这 8 种 内 存 间 操 作 , 保 证 了一 个 线 程 对 某 个 volatile 变 量 的 修 改 , 一 定 会 被 另 一 个 线 程 看 见 , 即 保 证 了 可 见 性 。

8 : 既 然 volatile 能 够 保 证 线 程 间 的 变 量 可 见 性 , 是 不 是 就 意 味 着 基 于 volatile 变 量 的 运 算 就 是 并 发 安 全 的 ?

显 然 不 是 的 。 基 于 volatile 变 量 的 运 算 在 并 发 下 不 一 定 是 安 全 的 。

volatile 变 量 在 各 个 线 程 的 工 作 内 存 , 不 存 在 一 致 性 问 题 ( 各 个 线 程 的 工 作 内 存 中 volatile 变 量 , 每 次 使 用 前 都 要 刷 新 到 主 内 存 ) 。

但 是 Java 里 面 的 运 算 并 非 原 子 操 作 , 导 致 volatile 变 量 的 运 算 在 并发 下 一 样 是 不 安 全 的 。

9 : 请 对 比 下 volatile 对 比 Synchronized 的 异 同 。

Synchronized 既 能 保 证 可 见 性 , 又 能 保 证 原 子 性 , 而 volatile 只 能

保 证 可 见 性 , 无 法 保 证 原 子 性 。

ThreadLocal 和 Synchonized 都 用 于 解 决 多 线 程 并 发 访 问 , 防 止 任 务

在 共 享 资 源 上 产 生 冲 突 。 但 是 ThreadLocal 与 Synchronized 有 本 质

的 区 别 。

Synchronized 用 于 实 现 同 步 机 制 , 是 利 用 锁 的 机 制 使 变 量 或 代 码 块 在 某 一 时 该 只 能 被 一 个 线 程 访 问 , 是 一 种 “ 以 时 间 换 空 间 ”的 方 式 。 而 ThreadLocal 为 每 一 个 线 程 都 提 供 了 变 量 的 副 本 , 使 得 每 个 线 程 在 某 一 时 间 访 问 到 的 并 不 是 同 一 个 对 象 , 根 除 了 对 变 量 的 共 享 , 是 一 种 “ 以 空 间 换 时 间 ” 的 方 式 。

10: 请 谈 谈 ThreadLocal 是 怎 么 解 决 并 发 安 全 的 ?

ThreadLocal 这 是 Java 提 供 的 一 种 保 存 线 程 私 有 信 息 的 机 制 , 因 为

其 在 整 个 线 程 生 命 周 期 内 有 效 , 所 以 可 以 方 便 地 在 一 个 线 程 关 联 的 不 同 业 务 模 块 之 间 传 递 信 息 , 比 如 事 务 ID、 Cookie 等 上 下 文 相 关 信 息 。ThreadLocal 为 每 一 个 线 程 维 护 变 量 的 副 本 , 把 共 享 数 据 的 可 见 范 围 限 制 在 同 一 个 线 程 之 内 , 其 实 现 原 理 是 , 在 ThreadLocal 类 中 有 一 个 Map, 用 于 存 储 每 一 个 线 程 的 变 量 的 副 本 。

11 : 很 多 人 都 说 要 慎 用 ThreadLocal, 谈 谈 你 的 理 解 , 使 用 ThreadLocal 需 要 注 意 些 什 么 ?

使 用 ThreadLocal 要 注 意 remove! ThreadLocal 的 实 现 是 基 于 一 个 所 谓 的 ThreadLocalMap, 在 ThreadLocalMap 中 , 它 的 key 是 一 个 弱 引 用 。

通 常 弱 引 用 都 会 和 引 用 队 列 配 合 清 理 机 制 使 用 , 但 是 ThreadLocal 是 个 例 外 , 它 并 没 有 这 么 做 。

这 意 味 着 , 废 弃 项 目 的 回 收 依 赖 于 显 式 地 触 发 , 否 则 就 要 等 待 线 程 结 束 , 进 而 回 收 相 应 ThreadLocalMap! 这 就 是 很 多 OOM 的 来 源 ,所 以 通 常 都 会 建 议 , 应 用 一 定 要 自 己 负 责 remove, 并 且 不 要 和 线 程 池 配

分享就到这里啦,喜欢的朋友们点赞,收藏,加关注哦!领取资料后台私聊小编:即可免费领取!

文章来源:智云一二三科技

文章标题:java中的线程池是如何实现的

文章地址:https://www.zhihuclub.com/191350.shtml

关于作者: 智云科技

热门文章

网站地图