文档章节

【06】竞态条件与临界区

秋雨霏霏
 秋雨霏霏
发布于 2017/07/22 18:31
字数 1250
阅读 30
收藏 0
点赞 0
评论 0

竞态条件 是一个可能在临界区中发生的特殊条件。 而临界区表示着,一段在并发执行和顺序执行时有着不同运行结果的代码片段。

临界区中多线程执行的结果可能会因不同的线程执行顺序,而导致不同的运行结果。 竞态条件就是源自线程同时竞争执行临界区,而竞争的结果也会影响着临界区的运行结果。

接下来再详细说说这两个概念。

临界区

在一个应用中运行多个线程,这本身不会有啥问题。 问题是出在,多个线程访问同一资源的时候。 例如:同一个内存区域(变量,数组,对象),系统资源(数据库,接口)以及文件等。

事实上,问题主要是线程对资源进行写操操作。 如果,多个线程都是执行资源读操作,那是不会有什么问题的。

下面这个例子就展示了一个临界区,当多个线程同时执行时,就会出问题:

public class Counter {

 protected long count = 0;

 public void add(long value){
     this.count = this.count + value;
 }
}

想象一下,如果有两个线程,A和B,在同一个实例上进行加法操作。 而我们无法知道操作是如何调度这两个线程的执行。 虽然只有一行代码,但在JVM中add()方法不会以原子的方式被执行。 而是会以下面的逻辑来处理:

  1. 从内存读取this.count的值到寄存器中
  2. 在寄存中执行加法指令操作
  3. 将计算结果从寄存器中写会内存

而当A,B两个线程交叉执行时,可能会得到如下的情况:

    this.count = 0;

A: 读取this.count到寄存器           (0)
B: 读取this.count到寄存器           (0)
B: 在寄存器中加上2                  
B: 将结果2写回内存。this.count=2    (2)
A: 在寄存器中加上3
A: 将结果3写回内存。this.count=3    (3)

两个线程分别想要对计数器累加2和3。所以,执行完成时预期值应该是5。 但是,两个线程的执行过程是交叉进行的,这样就会导致结果不同了。

如果按上面的顺序执行,最后A线程的结果会直接覆盖B线程的结果。

临界区中的竞态条件

上例中,add()方法就是一个临界区。当多个线程执行时,触发竞态条件。

就是说,两个线程争用同一个资源,此时,执行顺序就变得很敏感了,这也就称之为竞态条件。 导致竞态条件的代码块就称为临界区。

防止竞态条件

要想防止竞态条件的发生,就要确保临界区以原子方式执行。 这就意味着,一次只有一个线程在执行,在第一个线程离开临界区之前,不会有其他线程可以进入临界区。

为了避免竞态条件,可以对临界区使用一些同步机制。线程同步可以使用Java阻塞同步,还可以使用Lock以及原子变量来进行同步。

临界区吞吐量

在上例中,对整个临界区进行同步阻塞是可以解决问题的。一般来说,对于临界区是越小越好,这样可以让每一个线程执行更小的临界区。 为了减少对共享资源的争用,这样就可以增加总体上的吞吐量。

来看看这个例子:

public class TwoSums {
    
    private int sum1 = 0;
    private int sum2 = 0;
    
    public void add(int val1, int val2){
        synchronized(this){
            this.sum1 += val1;   
            this.sum2 += val2;
        }
    }
}

注意add()方法,对两个成员变量分别进行累加和。 为了防止累加和出现竞态条件,这里使用了一个Java synchronized 阻塞。 这样一次只会有一个线程能够进行累加和的计算。

但是,要注意,两个变量的累加计算,其实是互相独立的,其实是可以分成两个同步块:

public class TwoSums {
    
    private int sum1 = 0;
    private int sum2 = 0;

    private Integer sum1Lock = new Integer(1);
    private Integer sum2Lock = new Integer(2);

    public void add(int val1, int val2){
        synchronized(this.sum1Lock){
            this.sum1 += val1;   
        }
        synchronized(this.sum2Lock){
            this.sum2 += val2;
        }
    }
}

这样两个线程就可以并行进入add()方法。 一个进入第一个同步块,另一个进入第二个。 两个同步块分别在两个不同的对象上进行同步,所以两个不同的线程可以互不干扰的进入两个代码块中。 这样在add()方法中,就可以减少线程等待的时间。

当然,这个例子太简单了。在实际工作中,将共享资源分解到多个临界区,其实是很复杂的工作。 需要分析更多情况下的执行顺序。

© 著作权归作者所有

共有 人打赏支持
秋雨霏霏
粉丝 143
博文 90
码字总数 160569
作品 0
杭州
CTO(技术副总裁)
竞态条件与临界区

在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如,同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或...

LCZ777 ⋅ 2014/05/21 ⋅ 0

【Java并发性和多线程】竞态条件与临界区

本文为转载学习 原文链接:http://tutorials.jenkov.com/java-concurrency/race-conditions-and-critical-sections.html 译文链接:http://ifeve.com/race-conditions-and-critical-section......

heroShane ⋅ 2014/01/28 ⋅ 0

并发编程QA

1、什么时候应该使用多线程 1⃣️ 多CPU的情况 2⃣️ IO等待 如果有一个线程在执行的时候,遇到了磁盘读写或者网络传输阻塞,那么线程就需要等待,这时候占用的CPU可以释放,然后CPU就可以将...

whc20011 ⋅ 2016/10/18 ⋅ 0

[shell应用进阶]:限制同时运行脚本实例的个数 -- 串行化:换一个思路。

【背景介绍】 CU上曾经有几个帖子讨论到一个实际问题,就是如何限制同一时刻只允许一个脚本实例运行。其中本版新老斑竹和其它网友都参加了讨论,但以faintblue兄的帖子对大家启发最大,下面的...

红薯 ⋅ 2009/05/06 ⋅ 0

Java并发库(Java Concurrency)

原文地址 译文地址 译者:张坤等 Java并发性和多线程介绍(Java Concurrency / Multithreading Tutorial) 多线程的优点(Multithreading Benefits) 多线程的代价(Multithreading Costs) ...

暗之幻影 ⋅ 2016/12/17 ⋅ 0

linux设备驱动系列:如何处理竞态关系

综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争。 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时、并行...

东辉在线 ⋅ 2015/04/11 ⋅ 0

Linux驱动(五)linux设备驱动中的并发控制

在应用层学习时,我们学习过多个进程处理共享资源的情况。实际上在驱动中也有类似的情况,并且相对于应用层,并发的情况会更多。 并发(concurrency)指的是多个执行单元同时、并行被执行,而...

u012142460 ⋅ 01/09 ⋅ 0

linux设备驱动第五篇:驱动中的并发与竟态

综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争。 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时、并行...

HAOMCU ⋅ 2015/04/11 ⋅ 1

线程相关。

mutex and Condition: mutex为了保护临界区与竞态资源。condition,为了线程之间的相互工作流程 mutex 是线程之间公用的一把锁,只有一把锁,lock是线程对mutex进行锁住的一个行为。 在对象析...

invictus_lee ⋅ 2014/03/08 ⋅ 0

剖析同步器

虽然许多同步器(如锁,信号量,阻塞队列等)功能上各不相同,但它们的内部设计上却差别不大。换句话说,它们内部的的基础部分是相同(或相似)的。了解这些基础部件能在设计同步器的时候给我...

LCZ777 ⋅ 2014/05/26 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

tcp/ip详解-链路层

简介 设计链路层的目的: 为IP模块发送和接收IP数据报 为ARP模块发送ARP请求和接收ARP应答 为RARP模块发送RARP请求和接收RARP应答 TCP/IP支持多种链路层协议,如以太网、令牌环往、FDDI、RS-...

loda0128 ⋅ 43分钟前 ⋅ 0

spring.net aop代码例子

https://www.cnblogs.com/haogj/archive/2011/10/12/2207916.html

whoisliang ⋅ 59分钟前 ⋅ 0

发送短信如何限制1小时内最多发送11条短信

发送短信如何限制1小时内最多发送11条短信 场景: 发送短信属于付费业务,有时为了防止短信攻击,需要限制发送短信的频率,例如在1个小时之内最多发送11条短信. 如何实现呢? 思路有两个 截至到当...

黄威 ⋅ 昨天 ⋅ 0

mysql5.7系列修改root默认密码

操作系统为centos7 64 1、修改 /etc/my.cnf,在 [mysqld] 小节下添加一行:skip-grant-tables=1 这一行配置让 mysqld 启动时不对密码进行验证 2、重启 mysqld 服务:systemctl restart mysql...

sskill ⋅ 昨天 ⋅ 0

Intellij IDEA神器常用技巧六-Debug详解

在调试代码的时候,你的项目得debug模式启动,也就是点那个绿色的甲虫启动服务器,然后,就可以在代码里面断点调试啦。下面不要在意,这个快捷键具体是啥,因为,这个keymap是可以自己配置的...

Mkeeper ⋅ 昨天 ⋅ 0

zip压缩工具、tar打包、打包并压缩

zip 支持压缩目录 1.在/tmp/目录下创建目录(study_zip)及文件 root@yolks1 study_zip]# !treetree 11└── 2 └── 3 └── test_zip.txt2 directories, 1 file 2.yum...

蛋黄Yolks ⋅ 昨天 ⋅ 0

聊聊HystrixThreadPool

序 本文主要研究一下HystrixThreadPool HystrixThreadPool hystrix-core-1.5.12-sources.jar!/com/netflix/hystrix/HystrixThreadPool.java /** * ThreadPool used to executed {@link Hys......

go4it ⋅ 昨天 ⋅ 0

容器之上传镜像到Docker hub

Docker hub在国内可以访问,首先要创建一个账号,这个后面会用到,我是用126邮箱注册的。 1. docker login List-1 Username不能使用你注册的邮箱,要用使用注册时用的username;要输入密码 ...

汉斯-冯-拉特 ⋅ 昨天 ⋅ 0

SpringBoot简单使用ehcache

1,SpringBoot版本 2.0.3.RELEASE ①,pom.xml <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELE......

暗中观察 ⋅ 昨天 ⋅ 0

Spring源码解析(八)——实例创建(下)

前言 来到实例创建的最后一节,前面已经将一个实例通过不同方式(工厂方法、构造器注入、默认构造器)给创建出来了,下面我们要对创建出来的实例进行一些“加工”处理。 源码解读 回顾下之前...

MarvelCode ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部