线程的同步
来源:互联网 发布:qq微信头像知乎 编辑:程序博客网 时间:2024/06/05 06:11
在 单 线 程 程 序 中 , 每 次 只 能 做 一 件 事 情 , 后 面 的 事 情 需 要 等 待 前 面 的 事 情 完 成 后 才 可 以 进 行 : 但 是 如 果 使 用 多 线 程 程 序 , 就 会 发 生 两 个 线 程 抢 占 资 源 的 问 题 , 例 如 两 个 人 同 时 说 话 、 两 个 人 同 时 过 同 一 个 独 木 桥 等 。 所 以 在 多 线 程 编 程 中 , 需 要 防 止 这 些 资 访 问 的 冲 突 。 Java 提 供 线 程 同 步 的 机 制 来 防 止 资 源 访 问 的 冲 突 。
线 程 安 全
实 际 开 发 中 , 使 用 多 线 程 程 序 的 情 况 很 多 , 如 银 行 排 号 系 统 、 火 车 站 售 票 系 统 等 。 这 种 多 线 程 的 程 序 通 常 会 发 生 问 题 , 以 火 车 站 售 票 系 统 为 例 , 在 代 码 中 判 断 当 前 票 数 是 否 大 于 0 , 如 果 大 于 0 则 执 行 将 该 票 出 售 给 乘 客 功 能 , 但 当 两 个 线 程 同 时 访 问 这 段 代 码 时 ( 假 如 这 时 只 剩 下 一 张 票 ) , 第 一 个 线 程 将 票 售 出 , 与 此 同 时 第 二 个 线 程 也 已 经 执 行 完 成 判 断 是 否 有 票 的 操 作 , 并 得 出 结 论 票 数 大 于 0 , 于 是 它 也 执 行 售 出 操 作 , 这 样 就 会 产 生 负 数 。 所 以 在 编 写 多 线 程 程 序 时 , 应 该 考 虑 到 线 程 安 全 问 题 。 实 质 上 线 程 安 全 问 题 来 源 于 两 个 线 程 同 时 存 取 单 一 对 象 的 数 据 。
〖 例 〗 在 项 目 中 创 建 ThreadSafeTest 类 , 该 类 继 承 了 Runnable 类 , 主 要 实 现 模 拟 火 车 站 售 票 系 统 。
public class ThreadSafeTest implements Runnable{ int sum=10; //设置当前总票数 @Override public void run() { // TODO Auto-generated method stub while(true){ if(sum>0){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("车票还有"+(sum--)+"张车票"); } } } public static void main(String[] args) { //实例化对象 ThreadSafeTest tst=new ThreadSafeTest(); /* * 创建4个线程 */ Thread t1=new Thread(tst); Thread t2=new Thread(tst); Thread t3=new Thread(tst); Thread t4=new Thread(tst); /* * 启动线程 */ t1.start(); t2.start(); t3.start(); t4.start(); }}
结果:
车票还有10张车票
车票还有8张车票
车票还有9张车票
车票还有10张车票
车票还有7张车票
车票还有6张车票
车票还有5张车票
车票还有4张车票
车票还有3张车票
车票还有2张车票
车票还有1张车票
车票还有0张车票
车票还有-1张车票
车票还有-2张车票
结果分析: 我们发现最后售出的票为负值,这样说明这个程序出了问题。这是由于同时创建了4个线程,这四个线程在最后票数为1时,进入run方法,都判定sum变量大于0,但此时,4个线程都对它进行递减操作,因此产生了负值。
线 程 同 步 机 制
如 何 解 决 资 源 共 享 的 问 题 ? 基 本 上 所 有 解 决 多 线 程 资 源 冲 突 问 题 都 会 采 用 给 定 时 间 只 允 许 一 个 线 程 访 问 共 享 资 源 , 这 时 就 需 要 给 共 享 资 源 上 一 道 锁 。 这 就 好 比 一 个 人 上 洗 手 间 , 这 个 人 进 入 洗 手 间 后 将 门 锁 上 , 当 他 出 来 时 再 将 锁 打 开 , 然 后 其 他 人 才 可 以 进 入 。
同 步 块
在 Java 中 提 供 了 同 步 机 制 , 可 以 有 效 地 防 止 资 冲 突 。 同 步 机 制 使 用synchronized 关 键 字 。
〖 例 〗 创 建 CopyOfThreadSafeTest 类 , 使 对 num 操 作 的 代 码 设 置 在 同 步 块 中 。
public class CopyOfThreadSafeTest implements Runnable{ int num=10; @Override public void run() { while(true){ synchronized(""){ if(num>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("tickets"+--num); } } } } public static void main(String[] args) { CopyOfThreadSafeTest t=new CopyOfThreadSafeTest(); Thread t1=new Thread(t); Thread t2=new Thread(t); Thread t3=new Thread(t); Thread t4=new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); }}
结果:
tickets9
tickets8
tickets7
tickets6
tickets5
tickets4
tickets3
tickets2
tickets1
tickets0
结果分析:
打 印 到 最 后 票 数 没 有 出 现 负 数 , 这 是 因 为 将 资 源 放 置 在 了 同 步 块 中 ·。这 个 同 步 块 也 被 称 为 临 界 区 , 它 使 用 synchronized 关 键 字 建 立 。 其 语 法 格 式 如 下 :
synchronized(Object){}
通 常 是 将 共 享 资 源 的 操 作 放 置 在 synchronized 定 义 的 区 域 内 , 这 样 当 其 他 线 程 也 获 取 到 这 个 锁 时 , 必 须 等 待 锁 被 释 放 时 才 能 进 入 该 区 域 。 Object 为 任 意 一 个 对 象 , 每 个 对 象 都 存 在 一 个 标 志 位 , 并 具 有 两 个 值 , 分 别 为 0 和 1。 一 个 线 程 运 行 到 同 步 块 时 首 先 检 查 该 对 象 的 标 志 位 , 如 果 为 0 状 态 , 表 明 此 同 步 块 中 存 在 其 他 线 程 在 运 行 。 这 时 该 线 程 处 于 就 绪 状 态 , 直 到 处 于 同 步 块 中 的 线 程 执 行 完 同 步 块 中 的 代 码 为 止 。 这 时 该 对 象 的 标 识 位 被 设 置 为 1 , 该 线 程 才 能 执 行 同 步 块 中 的 代 码 , 并 将 Object 对 象 的 标 识 位 设 置 为 0 , 防 止 其 他 线 程 执 行 同 步 块 中 的 代 码 。
同步方法
同 步 方 法 就 是 在 方 法 前 面 修 饰 synchromzed 关 键 字 的 方 法 , 其 语 法 格 式 如 下 :
synchromzed void f(){}
当 某 个 对 象 调 用 了 同 步 方 法 时 , 该 对 象 上 的 其 他 同 步 方 法 必 须 等 待 该 同 步 方 法 执 行 完 毕 才 能 被 执 行 。 必 须 将 每 个 能 访 问 共 享 资 源 的 方 法 修 饰 为 synchronized , 否 则 就 会 出 错 。
〖 例 〗在 项 目 中 创 建 一 个 类 文 件 , 在 该 类 中 定 义 同 步 方 法 。
public class SynchronizedTest implements Runnable{ int num=10; public synchronized void doit(){ //定义同步方法 if(num>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("tickets"+--num); } } @Override public void run() { //在run()方法中调用该同步方法 while(true){ doit(); } } public static void main(String[] args) { SynchronizedTest st=new SynchronizedTest(); Thread t1=new Thread(st); Thread t2=new Thread(st); Thread t3=new Thread(st); Thread t4=new Thread(st); t1.start(); t2.start(); t3.start(); t4.start(); }}
结果:
tickets9
tickets8
tickets7
tickets6
tickets5
tickets4
tickets3
tickets2
tickets1
tickets0
分析:
将 共 享 的 资 源 放 置 在 同 步 方 法 中 , 运 行 结 果 与 使 用 同 步 块 的 结 果 一 致 。
- 线程的同步-同步方法
- 线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Java线程:线程的同步-同步块
- Java线程:线程的同步-同步方法
- Android RecyclerView 网格显示正方形元素
- Android Studio提交代码到SVN
- 关于 Context initialization failed org.springframework.beans.factory.BeanDefinitionStoreException 错误
- 3. Longest Substring Without Repeating Characters最长不重复子串
- awk的基本使用
- 线程的同步
- 安卓跳入系统分享
- bzoj 4407: 于神之怒加强版
- vue从零开始
- python基础---列表表达式
- 欢迎使用CSDN-markdown编辑器
- vim 打开显示行号
- 几种常见的单例
- Python3爬虫实战之爬取京东图书图片