文档章节

2017-09-08-Java并发笔记之 Race Condition and Critical Section

kailuncen
 kailuncen
发布于 2017/09/08 11:48
字数 1006
阅读 11
收藏 0

个人介绍

Java爱好者,个人网站: http://kailuncen.me/about/

前言

这几天学习并发编程,race-conditions-and-critical-sections,翻译一下,写点自己的笔记并加上点个人的理解。

网页中里中提到两个名词Race Condition 和 Critical Section,接下来对他们进行解释和例子演示。

Race Condition

在多线程场景下,当多个线程访问同一块资源,且执行结果与线程访问的先后顺序相关,即表明这里面存在着Race Condition,中文翻译即竞争条件。

看下面👇的代码,多个线程都会调用add方法对同一个count值进行加法。

  public class Counter {

     protected long count = 0;

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

然而,add方法中的加法需要好几个步骤才能完成。

1. 从内存中读取count的值到寄存器。
2. 加value。
3. 写回内存。

如果有两个线程都对add方法进行了操作,比如线程A加3,线程B加2,我们的预期结果是5。由于线程的访问顺序以及切换的时间是不可预期的,在特定的访问顺序下,可能出现一些出乎意料的结果,比如下文中的执行顺序。

A:  Reads this.count into a register (0)
B:  Reads this.count into a register (0)
B:  Adds value 2 to register
B:  Writes register value (2) back to memory. this.count now equals 2
A:  Adds value 3 to register
A:  Writes register value (3) back to memory. this.count now equals 3

由于加法不是原子性的,在加法执行过程中的每一步都可能存在着线程切换。 比如线程A和B都先后读到0,然后线程B占用了时间片完成了加2的操作,写回了内存,此时内存中count的值等于2。 然后线程A重新得到调度,此时线程A内部的count值还是0,线程A对主内存内count的变化是不可见的,然后线程完成加3操作,写回内存,此时count值等于3。

上述代码中的add方法内部就存在着竞争条件,会根据线程执行顺序的不确定性影响最后的执行结果。

Critical Section

我们把会导致Race Condition的区域称为Critical Section,中文翻译临界区。临界区即每个线程中访问临界资源的那段代码。

在上文的代码中,this.count就是临界资源

this.count = this.count + value

就是临界区,为了保证执行结果的正确性,避免临界区内产生竞争条件,我们需要确保临界区内的执行是原子的,每次仅允许一个线程进去,进入后不允许其他线程进入。

我们可以采用线程同步做到以上的要求,线程同步可以使用synchronized同步代码,或者locks,或者是原子变量比如AtomicInteger等。

可以把整个临界区使用synchronized同步,但把临界区拆分成多个小的临界区能够降低对共享资源的争夺,增加整个临界区的吞吐量,下面举个例子。

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;
        }
    }
}

在上述代码中,简单的做法就是锁住整个对象,只有一个线程能够执行两个不同变量的加法操作。然而,由于这两个变量是互相独立的,可以拆分到两个不同的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方法中操作,一个线程在第一个synchronized块,另一个线程在第二个synchronized块,两个synchronized块同步的是不同的对象,所以两个线程可以独立执行,整体线程等待的时间会变少,吞吐量能够得到提升。

© 著作权归作者所有

共有 人打赏支持
kailuncen
粉丝 88
博文 17
码字总数 28778
作品 0
卢湾
后端工程师
私信 提问
Disruptor 3.3.11 和 3.4.2 发布,并发编程框架

Disruptor 是一个 Java 的并发编程框架,大大的简化了并发程序开发的难度,在性能上也比 Java 本身提供的一些并发包要好。本次更新主要是 bug 修复,具体如下: 3.4.2 Fix race condition i...

淡漠悠然
03/23
962
7
读书笔记之《Java并发编程的艺术》-java中的锁

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
0
15个线程方面的面试题

1) You have thread T1, T2 and T3, how will you ensure that thread T2 run after T1 and thread T3 run after T2? 2) What is the advantage of new Lock interface over synchronized bl......

lateron
2013/05/31
1K
28
0015-如何使用Sentry管理Hive外部表权限

1.文档编写目的 本文档主要讲述如何使用Sentry对Hive外部表权限管理,并基于以下假设: 1.操作系统版本:RedHat6.5 2.CM版本:CM 5.11.1 3.集群已启用Kerberos和Sentry 4.采用具有sudo权限的...

Hadoop实操
11/17
0
0
kernel BUG at fs/xfs/xfs_aops.c:1062!

转自: https://bugs.centos.org/view.php?id=14073 RHEL7: kernel crash in xfsvmwritepage - kernel BUG at fs/xfs/xfs_aops.c:1062! SOLUTION VERIFIED - Updated August 17 2017 at 3:52......

jia_xiaolei
07/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

stylus

stylus基础教程,stylus实例教程,stylus语法总结

miaojiangmin
13分钟前
0
0
PHP生成CSV之内部换行

当我们使用PHP将采集到的文件内容保存到csv文件时,往往需要将采集内容进行二次过滤处理才能得到需要的内容。比如网页中的换行符,空格符等等。 对于空格等处理起来都比较简单,这里我们单独...

豆花饭烧土豆
今天
2
0
使用 mjml 生成 thymeleaf 邮件框架模板

发邮件算是系统开发的一个基本需求了,不过搞邮件模板实在是件恶心事,估计搞过的同仁都有体会。 得支持多种客户端 支持响应式 疼彻心扉的 outlook 多数客户端只支持 inline 形式的 css 布局...

郁也风
今天
8
0
让哲学照亮我们的人生——读《医务工作者需要学点哲学》有感2600字

让哲学照亮我们的人生——读《医务工作者需要学点哲学》有感2600字: 作者:孙冬梅;以前读韩国前总统朴槿惠的著作《绝望锻炼了我》时,里面有一句话令我印象深刻,她说“在我最困难的时期,...

原创小博客
今天
5
0
JAVA-四元数类

public class Quaternion { private final double x0, x1, x2, x3; // 四元数构造函数 public Quaternion(double x0, double x1, double x2, double x3) { this.x0 = ......

Pulsar-V
今天
20
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部